Skip to content

Commit c751407

Browse files
authored
Merge pull request #1182 from rackerlabs/PUC-1095-20250820
feat: Adds post-deploy OpenStack Octavia playbook
2 parents 0c72c08 + 9a8ba4e commit c751407

File tree

10 files changed

+301
-1
lines changed

10 files changed

+301
-1
lines changed

ansible/playbooks/openstack_network.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515

16-
- name: Openstack Network
16+
- name: OpenStack Network
1717
hosts: neutron
1818
connection: local
1919

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
# Copyright (c) 2025 Rackspace Technology, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
- name: OpenStack Octavia Post Deployment
17+
hosts: neutron
18+
connection: local
19+
20+
pre_tasks:
21+
- name: Fail if ENV variables are not set
22+
ansible.builtin.fail:
23+
msg: "Environment variable {{ item }} is not set. Exiting playbook."
24+
when: lookup('env', item) == ''
25+
loop:
26+
- OS_CLOUD
27+
28+
roles:
29+
- role: openstack_octavia
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
openstack_octavia_load_balancer_network_name: "lb-mgmt-net"
3+
openstack_octavia_load_balancer_subnet_name: "lb-mgmt-subnet"
4+
openstack_octavia_load_balancer_subnet: "172.31.0.0/24"
5+
openstack_octavia_load_balancer_subnet_start: "172.31.0.2"
6+
openstack_octavia_load_balancer_subnet_end: "172.31.0.200"
7+
openstack_octavia_provider_network_type: "ovn"
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
3+
- name: Get network info
4+
openstack.cloud.networks_info:
5+
name: "{{ openstack_octavia_load_balancer_network_name }}"
6+
register: existing_networks
7+
run_once: true
8+
9+
- name: Create network if not exists
10+
openstack.cloud.network:
11+
name: "{{ openstack_octavia_load_balancer_network_name }}"
12+
provider_network_type: "{{ openstack_octavia_provider_network_type }}"
13+
state: present
14+
when: existing_networks | length == 0
15+
run_once: true
16+
17+
- name: Get network info again with the ID this time
18+
openstack.cloud.networks_info:
19+
name: "{{ openstack_octavia_load_balancer_network_name }}"
20+
register: existing_networks_again
21+
run_once: true
22+
23+
- name: Create a new subnet in neutron
24+
openstack.cloud.subnet:
25+
network_name: "{{ openstack_octavia_load_balancer_network_name }}"
26+
name: "{{ openstack_octavia_load_balancer_subnet_name }}"
27+
cidr: "{{ openstack_octavia_load_balancer_subnet }}"
28+
allocation_pool_start: "{{ openstack_octavia_load_balancer_subnet_start }}"
29+
allocation_pool_end: "{{ openstack_octavia_load_balancer_subnet_end }}"
30+
state: present
31+
run_once: true
32+
33+
- name: Create a security group
34+
openstack.cloud.security_group:
35+
name: lb-mgmt-sec-grp
36+
state: present
37+
description: security group for octavia load balancers
38+
run_once: true
39+
40+
- name: Create a security group rule
41+
openstack.cloud.security_group_rule:
42+
security_group: lb-mgmt-sec-grp
43+
protocol: icmp
44+
remote_ip_prefix: 0.0.0.0/0
45+
run_once: true
46+
47+
- name: Create a security group rule
48+
openstack.cloud.security_group_rule:
49+
security_group: lb-mgmt-sec-grp
50+
protocol: tcp
51+
port_range_min: 22
52+
port_range_max: 22
53+
remote_ip_prefix: 0.0.0.0/0
54+
run_once: true
55+
56+
- name: Create a security group rule
57+
openstack.cloud.security_group_rule:
58+
security_group: lb-mgmt-sec-grp
59+
protocol: tcp
60+
port_range_min: 9443
61+
port_range_max: 9443
62+
remote_ip_prefix: 0.0.0.0/0
63+
run_once: true
64+
65+
- name: Create a security group for octavia health manager
66+
openstack.cloud.security_group:
67+
name: lb-health-mgr-sec-grp
68+
state: present
69+
description: security group for octavia health manager
70+
run_once: true
71+
register: security_group
72+
73+
- name: Create health manager group security rules
74+
openstack.cloud.security_group_rule:
75+
security_group: lb-health-mgr-sec-grp
76+
protocol: udp
77+
port_range_min: 5555
78+
port_range_max: 5555
79+
remote_ip_prefix: 0.0.0.0/0
80+
run_once: true
81+
82+
- name: Create ports debug
83+
ansible.builtin.debug:
84+
msg: "item {{ item }}"
85+
with_items: "{{ groups['all']
86+
| map('extract', hostvars)
87+
| selectattr('ovs_enabled', 'defined')
88+
| selectattr('ovs_enabled', 'equalto', true)
89+
| map(attribute='inventory_hostname')
90+
| list }}"
91+
92+
- name: Build octavia_port_names (override or hostname)
93+
ansible.builtin.set_fact:
94+
octavia_port_names: "{{ octavia_port_names | default([]) + [ (hostvars[item].octavia_host_override | default(item)) ] }}"
95+
loop: "{{ groups['all'] }}"
96+
when:
97+
- hostvars[item].ovs_enabled is defined
98+
- hostvars[item].ovs_enabled | bool
99+
run_once: true
100+
delegate_to: localhost
101+
loop_control:
102+
label: "{{ item }}"
103+
104+
- name: Create ports debug
105+
ansible.builtin.debug:
106+
msg: "item {{ item }}"
107+
with_items: "{{ octavia_port_names }}"
108+
run_once: true
109+
110+
# ansible's openstack.cloud.port module doesn't permit you to specify
111+
# the binding:host_id, so we have to do it the hard way using
112+
# openstack.cloud.resource.
113+
- name: Create Octavia health manager port (set binding host_id)
114+
openstack.cloud.resource:
115+
state: present
116+
service: network
117+
type: port
118+
attributes:
119+
name: "octavia-health-manager-port-{{ item }}"
120+
network_id: "{{ existing_networks_again.networks[0].id }}"
121+
device_owner: "Octavia:health-mgr"
122+
binding_host_id: "{{ item }}"
123+
security_groups:
124+
- "{{ security_group.security_group.id }}"
125+
register: create_octavia_port
126+
with_items: "{{ octavia_port_names }}"
127+
run_once: true

components/octavia/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ kind: Kustomization
55
resources:
66
- octavia-rabbitmq-queue.yaml
77
- octavia-mariadb-db.yaml
8+
- octavia-post-deployment-job.yaml
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
apiVersion: batch/v1
3+
kind: Job
4+
metadata:
5+
name: octavia-post-deployment-job
6+
generateName: octavia-post-deployment-job-
7+
annotations:
8+
argocd.argoproj.io/hook: PostSync
9+
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
10+
spec:
11+
template:
12+
spec:
13+
containers:
14+
- name: octavia-post-deploy
15+
image: ghcr.io/rackerlabs/understack/ansible:latest
16+
imagePullPolicy: Always
17+
command: ["ansible-runner", "run", "/runner", "-vvv", "--playbook", "openstack_octavia.yaml"]
18+
env:
19+
- name: OS_CLOUD
20+
value: understack
21+
volumeMounts:
22+
- name: ansible-inventory
23+
mountPath: /runner/inventory/hosts.yaml
24+
subPath: hosts.yaml
25+
- name: ansible-kubernetes-inventory
26+
mountPath: /runner/inventory/inventory.yaml
27+
subPath: inventory.yaml
28+
- name: ansible-group-vars
29+
mountPath: /runner/inventory/group_vars/
30+
- name: openstack-svc-acct
31+
mountPath: /etc/openstack
32+
readOnly: true
33+
volumes:
34+
- name: runner-data
35+
emptyDir: {}
36+
- name: ansible-inventory
37+
configMap:
38+
name: ansible-inventory
39+
- name: ansible-kubernetes-inventory
40+
configMap:
41+
name: ansible-kubernetes-inventory
42+
- name: ansible-group-vars
43+
configMap:
44+
name: ansible-group-vars
45+
- name: openstack-svc-acct
46+
secret:
47+
secretName: openstack-svc-acct
48+
restartPolicy: OnFailure
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Load Balancer Configuration
2+
3+
If you decide to use Octavia Load Balancers there is a post-install ansible
4+
playbook which sets up networks, security groups, and ports for octavia's
5+
use.
6+
7+
The ansible playbook is `understack/ansible/playbooks/openstack_octavia.yaml`
8+
and it gets triggered after ArgoCD successfully syncs the octavia component
9+
from an ArgoCD post sync hook:
10+
11+
``` yaml
12+
metadata:
13+
annotations:
14+
argocd.argoproj.io/hook: PostSync
15+
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
16+
```
17+
18+
The post-sync job is `components/octavia/octavia-post-deployment-job.yaml`.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Testing and Verification
2+
3+
After deploying a new Understack, we have a few tools available to help us test our new deployment
4+
and ensure everything is working.
5+
6+
## OpenStack Tempest
7+
8+
We'll use [OpenStack Tempest](https://docs.openstack.org/tempest/latest/index.html)
9+
to quickly perform API tests against Understack.
10+
11+
Here's a small example using tempest to run keypair tests against an Understack instance:
12+
13+
``` text
14+
$ tempest run --concurrency 1 --serial --include-list include-keypair.txt
15+
{0} tempest.api.compute.keypairs.test_keypairs.KeyPairsV2TestJSON.test_get_keypair_detail [1.563604s] ... ok
16+
{0} tempest.api.compute.keypairs.test_keypairs.KeyPairsV2TestJSON.test_keypair_create_delete [0.884011s] ... ok
17+
{0} tempest.api.compute.keypairs.test_keypairs.KeyPairsV2TestJSON.test_keypair_create_with_pub_key [0.893123s] ... ok
18+
{0} tempest.api.compute.keypairs.test_keypairs.KeyPairsV2TestJSON.test_keypairs_create_list_delete [3.178549s] ... ok
19+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_create_keypair_invalid_name [0.589208s] ... ok
20+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_create_keypair_when_public_key_bits_exceeds_maximum [0.432699s] ... ok
21+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_create_keypair_with_duplicate_name [1.471715s] ... ok
22+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_create_keypair_with_empty_name_string [0.540209s] ... ok
23+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_create_keypair_with_empty_public_key [0.440568s] ... ok
24+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_create_keypair_with_long_keynames [0.435756s] ... ok
25+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_keypair_create_with_invalid_pub_key [0.479691s] ... ok
26+
{0} tempest.api.compute.keypairs.test_keypairs_negative.KeyPairsNegativeTestJSON.test_keypair_delete_nonexistent_key [0.509478s] ... ok
27+
28+
======
29+
Totals
30+
======
31+
Ran: 12 tests in 12.3828 sec.
32+
- Passed: 12
33+
- Skipped: 0
34+
- Expected Fail: 0
35+
- Unexpected Success: 0
36+
- Failed: 0
37+
Sum of execute time for each test: 11.4186 sec.
38+
39+
==============
40+
Worker Balance
41+
==============
42+
- Worker 0 (12 tests) => 0:00:12.382768
43+
```
44+
45+
## OpenStack Rally
46+
47+
We'll also use [OpenStack Rally](https://docs.openstack.org/rally/latest/) to run integration and scenario tests.
48+
49+
See the [understack-tests](https://github.com/rackerlabs/understack/tree/main/python/understack-tests) in the understack repo
50+
for docs on running our rally scenarios.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Deployment Troubleshooting
2+
3+
We have documentation for individual components in the [Operator Guide](../operator-guide/index.md).
4+
5+
## ArgoCD Stuck Syncing
6+
7+
We have seen ArgoCD can sometimes become "stuck" while processing deployments. Sometimes
8+
this can be due to the app component running a kubernetes job which gets stuck indefinitely.
9+
10+
For example, some openstack-helm components have init jobs which depends on other steps being
11+
completed or may have a misconfiguration, where the init will loop forever. In argo you can
12+
see what jobs are stuck, then check the kubernetes job pod logs for further details. Note that
13+
a lot of OpenStack pods may have multiple containers, so interesting logs may not be in the
14+
default container output.
15+
16+
In argo, we've also seen it can be helpful to terminate an old sync and issue a new sync.

mkdocs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ nav:
136136
- deploy-guide/config-argo-workflows.md
137137
- Starting the Deployment:
138138
- deploy-guide/management-cluster.md
139+
- Post Deployment:
140+
- deploy-guide/load-balancers.md
141+
- deploy-guide/testing-verification.md
142+
- deploy-guide/troubleshooting.md
139143
- Further Actions:
140144
- deploy-guide/extra-sites.md
141145
- deploy-guide/add-remove-app.md

0 commit comments

Comments
 (0)