Skip to content

Commit 3ac15a3

Browse files
committed
Add hotstack CI Job - sno-2-bm
Single Node OpenShift (SNO), Sushy Emulator, 2x Ironic Nodes Assisted-By: Claude (claude-4.5-sonnet) Signed-off-by: Harald Jensås <hjensas@redhat.com>
1 parent 4bd0f16 commit 3ac15a3

File tree

18 files changed

+4684
-0
lines changed

18 files changed

+4684
-0
lines changed

ci/scenarios/sno-2-bm/README.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# sno-2-bm Scenario
2+
3+
## Overview
4+
5+
A Single Node OpenShift (SNO) scenario designed to test OpenStack Ironic bare
6+
metal provisioning with 2 dedicated Ironic nodes. This scenario validates the
7+
complete OpenStack bare metal lifecycle including node enrollment,
8+
provisioning, and comprehensive Tempest testing.
9+
10+
## Architecture
11+
12+
<!-- markdownlint-disable MD013 -->
13+
```mermaid
14+
graph TD
15+
Internet[("Internet")]
16+
Router{{"Neutron<br/>Router"}}
17+
18+
MachineNet["Machine Network<br/>192.168.32.0/24"]
19+
CtlPlane["CtlPlane Network<br/>192.168.122.0/24"]
20+
VLANNets["VLAN Trunk Networks<br/>Internal API: 172.17.0.0/24<br/>Storage: 172.18.0.0/24<br/>Tenant: 172.19.0.0/24"]
21+
IronicNet["Ironic Network<br/>172.20.1.0/24"]
22+
23+
Controller["Controller<br/>192.168.32.254<br/>DNS/HAProxy"]
24+
Master["SNO Master<br/>192.168.32.10"]
25+
IronicNodes["Ironic Nodes x2<br/>Virtual Baremetal"]
26+
27+
LVM["TopoLVM<br/>20GB"]
28+
CinderVols["Cinder Volumes x3<br/>20GB each"]
29+
30+
Internet --- Router
31+
32+
Router --- MachineNet
33+
Router --- CtlPlane
34+
Router --- VLANNets
35+
Router --- IronicNet
36+
37+
MachineNet --- Controller
38+
MachineNet --- Master
39+
CtlPlane --- Master
40+
VLANNets --- Master
41+
IronicNet --- Master
42+
IronicNet --- IronicNodes
43+
44+
Master --- LVM
45+
Master --- CinderVols
46+
47+
style Controller fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
48+
style Master fill:#F5A623,stroke:#C87D0E,stroke-width:3px,color:#fff
49+
style IronicNodes fill:#9B59B6,stroke:#6C3A82,stroke-width:2px,color:#fff
50+
style Router fill:#27AE60,stroke:#1E8449,stroke-width:3px,color:#fff
51+
```
52+
<!-- markdownlint-enable MD013 -->
53+
54+
### Component Details
55+
56+
- **Controller**: Hotstack controller providing DNS, load balancing, and
57+
orchestration services
58+
- **SNO Master**: Single-node OpenShift cluster running the complete OpenStack
59+
control plane
60+
- **Ironic Nodes**: 2 virtual bare metal nodes for testing Ironic provisioning workflows
61+
62+
## Features
63+
64+
- OpenStack Ironic bare metal provisioning service
65+
- Virtual BMC using sushy-tools for RedFish emulation
66+
- Comprehensive Tempest testing (scenario and API tests)
67+
- Complete OpenStack service stack (Nova, Neutron, Glance, Swift, etc.)
68+
- TopoLVM for local storage management
69+
- Multi-network setup for OpenStack services
70+
- Automatic node enrollment and lifecycle management
71+
72+
## Networks
73+
74+
- **machine-net**: 192.168.32.0/24 (OpenShift cluster network)
75+
- **ctlplane-net**: 192.168.122.0/24 (OpenStack control plane)
76+
- **internal-api-net**: 172.17.0.0/24 (OpenStack internal services)
77+
- **storage-net**: 172.18.0.0/24 (Storage backend communication)
78+
- **tenant-net**: 172.19.0.0/24 (Tenant network traffic)
79+
- **ironic-net**: 172.20.1.0/24 (Bare metal provisioning network)
80+
81+
## OpenStack Services
82+
83+
This scenario deploys a comprehensive OpenStack environment:
84+
85+
### Core Services
86+
87+
- **Keystone**: Identity service with LoadBalancer on Internal API
88+
- **Nova**: Compute service with Ironic driver for bare metal
89+
- **Neutron**: Networking service with OVN backend
90+
- **Glance**: Image service with Swift backend
91+
- **Swift**: Object storage service
92+
- **Placement**: Resource placement service
93+
94+
### Bare Metal Services
95+
96+
- **Ironic**: Bare metal provisioning service
97+
- **Ironic Inspector**: Hardware inspection service
98+
- **Ironic Neutron Agent**: Network management for bare metal
99+
100+
## Ironic Testing
101+
102+
### Node Configuration
103+
104+
- **2 Ironic Nodes**: Virtual instances with sushy-tools RedFish BMC
105+
- **Flavor**: hotstack.medium (configurable)
106+
- **Network**: Connected to dedicated Ironic provisioning network
107+
108+
### Test Scenarios
109+
110+
The scenario includes Tempest testing:
111+
112+
#### Scenario Tests
113+
114+
- Baremetal basic operations testing
115+
- Instance lifecycle management
116+
- Network connectivity validation
117+
- Power management testing
118+
119+
#### API Tests
120+
121+
- Ironic API functionality validation
122+
- Node management operations
123+
- Port and allocation management
124+
- Hardware inspection workflows
125+
126+
## Storage Configuration
127+
128+
- **TopoLVM**: Local volume management for OpenStack services
129+
- **Cinder Volumes**: Additional block storage on `/dev/vdc`, `/dev/vdd`, `/dev/vde`
130+
- **Swift Storage**: Object storage for Glance images
131+
- **Database Storage**: Persistent storage for Galera clusters
132+
133+
## Usage
134+
135+
```bash
136+
# Deploy the scenario
137+
ansible-playbook -i inventory.yml bootstrap.yml \
138+
-e @scenarios/sno-2-bm/bootstrap_vars.yml \
139+
-e @~/cloud-secrets.yaml
140+
141+
# Run comprehensive tests
142+
ansible-playbook -i inventory.yml 06-test-operator.yml \
143+
-e @scenarios/sno-2-bm/bootstrap_vars.yml \
144+
-e @~/cloud-secrets.yaml
145+
```
146+
147+
## Ironic Boot Interface
148+
149+
Three boot interface modes are supported for the virtual Ironic nodes:
150+
151+
- **`redfish-virtual-media`** (default): Virtual media boot via sushy-tools. Uses `heat_template.yaml`.
152+
- **`ipxe`**: Rescue-based iPXE network boot via sushy-tools Nova rescue mode. Uses `heat_template_ipxe.yaml`.
153+
- **`pxe`**: Traditional PXE boot with TFTP/shim (BIOS mode). Uses `heat_template_pxe.yaml`.
154+
155+
To switch modes, set `stack_template_path` in `bootstrap_vars.yml` to point to the desired template.
156+
157+
## Configuration Files
158+
159+
- `bootstrap_vars.yml`: Infrastructure and OpenShift configuration.
160+
- `automation-vars.yml`: Hotloop deployment stages
161+
- `heat_template.yaml`: OpenStack infrastructure template (redfish-virtual-media)
162+
- `heat_template_ipxe.yaml`: OpenStack infrastructure template (iPXE boot)
163+
- `heat_template_pxe.yaml`: OpenStack infrastructure template (PXE boot, BIOS mode)
164+
- `manifests/control-plane/control-plane.yaml`: OpenStack service configuration
165+
- `test-operator/automation-vars.yml`: Comprehensive test automation
166+
- `test-operator/tempest-tests.yml`: Tempest test specifications
167+
168+
This scenario provides a complete environment for validating OpenStack bare
169+
metal provisioning capabilities in a single-node OpenShift deployment with
170+
comprehensive testing automation.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
---
2+
stages:
3+
- name: TopoLVM Dependencies
4+
stages: >-
5+
{{
6+
lookup("ansible.builtin.template",
7+
"common/stages/topolvm-deps-stages.yaml.j2")
8+
}}
9+
10+
- name: Dependencies
11+
stages: >-
12+
{{
13+
lookup("ansible.builtin.template",
14+
"common/stages/deps-stages.yaml.j2")
15+
}}
16+
17+
- name: Cinder LVM
18+
stages: >-
19+
{{
20+
lookup("ansible.builtin.file",
21+
"common/stages/cinder-lvm-label-stages.yaml")
22+
}}
23+
24+
- name: TopoLVM
25+
stages: >-
26+
{{
27+
lookup("ansible.builtin.template",
28+
"common/stages/topolvm-stages.yaml.j2")
29+
}}
30+
31+
- name: OLM Openstack
32+
stages: >-
33+
{{
34+
lookup("ansible.builtin.template",
35+
"common/stages/olm-openstack-stages.yaml.j2")
36+
}}
37+
38+
- name: NodeNetworkConfigurationPolicy (nncp)
39+
documentation: |
40+
Apply node network configuration policies to configure host networking.
41+
Waits for all policies to be successfully configured.
42+
j2_manifest: manifests/control-plane/networking/nncp.yaml.j2
43+
wait_conditions:
44+
- >-
45+
oc wait -n openstack nncp -l osp/nncm-config-type=standard
46+
--for jsonpath='{.status.conditions[0].reason}'=SuccessfullyConfigured
47+
--timeout=180s
48+
49+
- name: NetworkAttchmentDefinition (NAD)
50+
documentation: |
51+
Create network attachment definitions for OpenStack services.
52+
Defines additional network interfaces for pods.
53+
manifest: manifests/control-plane/networking/nad.yaml
54+
55+
- name: MetalLB - L2Advertisement and IPAddressPool
56+
documentation: |
57+
Configure MetalLB load balancer with IP address pools and L2 advertisements.
58+
Enables external access to OpenStack services.
59+
manifest: manifests/control-plane/networking/metallb.yaml
60+
61+
- name: OpenstackControlPlane
62+
documentation: |
63+
Deploy the OpenStack control plane with all core services.
64+
Waits for the control plane to be fully ready before proceeding.
65+
j2_manifest: manifests/control-plane/control-plane.yaml.j2
66+
wait_conditions:
67+
- >-
68+
oc -n openstack wait openstackcontrolplanes.core.openstack.org controlplane
69+
--for condition=OpenStackControlPlaneDNSReadyCondition --timeout=600s
70+
71+
- name: Extra DNS LoadBalancer on Ironic network
72+
documentation: |
73+
Deploy additional DNS service on the Ironic network for bare metal provisioning.
74+
Provides DNS resolution for ironic nodes during deployment and inspection.
75+
manifest: manifests/control-plane/dnsmasq-dns-ironic.yaml
76+
wait_conditions:
77+
- >-
78+
oc wait -n openstack service dnsmasq-dns-ironic
79+
--for jsonpath='.status.loadBalancer' --timeout=60s
80+
81+
- name: Wait for OpenstackControlPlane
82+
documentation: |
83+
Wait for the OpenStack control plane to be fully ready and operational.
84+
Ensures all services are running before proceeding with additional configurations.
85+
wait_conditions:
86+
- >-
87+
oc wait -n openstack openstackcontrolplane controlplane
88+
--for condition=Ready --timeout=30m
89+
90+
- name: Update openstack-operators OLM
91+
stages: >-
92+
{{
93+
lookup('ansible.builtin.template',
94+
'common/stages/openstack-olm-update.yaml.j2')
95+
}}
96+
run_conditions:
97+
- >-
98+
{{
99+
openstack_operators_update is defined and
100+
openstack_operators_update | bool
101+
}}
102+
103+
- name: Wait for condition MinorUpdateAvailable True
104+
documentation: |
105+
Wait for OpenStack version to indicate a minor update is available.
106+
Required before proceeding with version updates.
107+
wait_conditions:
108+
- >-
109+
oc -n openstack wait openstackversions.core.openstack.org controlplane
110+
--for=condition=MinorUpdateAvailable=True --timeout=10m
111+
run_conditions:
112+
- "{{ openstack_update is defined and openstack_update | bool }}"
113+
114+
- name: "Minor update :: Create OpenStackVersion patch"
115+
documentation: |
116+
This creates a patch file `{{ manifests_dir }}/patches/openstack_version_patch.yaml`
117+
If `openstack_update_custom_images` is defined it will populate the customContainerImages
118+
in the OpenstackVersion YAML patch.
119+
shell: >-
120+
{{
121+
lookup('ansible.builtin.template',
122+
'common/scripts/create_openstack_version_patch.sh.j2')
123+
}}
124+
run_conditions:
125+
- "{{ openstack_update is defined and openstack_update | bool }}"
126+
127+
- name: "Minor update :: Update the target version in the OpenStackVersion custom resource (CR)"
128+
documentation: |
129+
The `hotstack-openstack-version-patch` script will get the `availableVersion`
130+
and us it to replace the string `__TARGET_VERSION__` in the patch file and
131+
apply the patch using `oc patch` command.
132+
command: >-
133+
hotstack-openstack-version-patch --namespace openstack --name controlplane
134+
--file {{ manifests_dir }}/patches/openstack_version_patch.yaml
135+
wait_conditions:
136+
- oc -n openstack wait openstackversions.core.openstack.org controlplane
137+
--for=condition=Ready --timeout=10m
138+
run_conditions:
139+
- "{{ openstack_update is defined and openstack_update | bool }}"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
os_cloud: default
3+
os_floating_network: public
4+
os_router_external_network: public
5+
6+
scenario: sno-2-bm
7+
scenario_dir: scenarios
8+
stack_template_path: "{{ scenario_dir }}/{{ scenario }}/heat_template_mixed.yaml"
9+
automation_vars_file: "{{ scenario_dir }}/{{ scenario }}/automation-vars.yml"
10+
test_operator_automation_vars_file: "{{ scenario_dir }}/{{ scenario }}/test-operator/automation-vars.yml"
11+
12+
openstack_operators_image: quay.io/openstack-k8s-operators/openstack-operator-index:latest
13+
openstack_operator_channel: alpha
14+
openstack_operator_starting_csv: null
15+
16+
openshift_version: stable-4.18
17+
18+
ntp_servers: []
19+
dns_servers:
20+
- 172.31.0.129
21+
22+
pull_secret_file: ~/pull-secret.txt
23+
24+
ovn_k8s_gateway_config_host_routing: true
25+
enable_iscsi: true
26+
enable_multipath: true
27+
28+
cinder_volume_pvs:
29+
- /dev/vdc
30+
- /dev/vdd
31+
- /dev/vde
32+
33+
# Nova console recorder NFS settings
34+
nova_console_recorder_nfs_server: controller-0.openstack.lab
35+
nova_console_recorder_nfs_path: /export/nova-console-recordings
36+
37+
stack_name: "hs-{{ scenario }}-{{ zuul.build[:8] | default('no-zuul') }}"
38+
stack_parameters:
39+
# On misconfigured clouds, uncomment these to avoid issues.
40+
# Ref: https://access.redhat.com/solutions/7059376
41+
# net_value_specs:
42+
# mtu: 1442
43+
dns_servers: "{{ dns_servers }}"
44+
ntp_servers: "{{ ntp_servers }}"
45+
controller_ssh_pub_key: "{{ controller_ssh_pub_key | default('') }}"
46+
router_external_network: "{{ os_router_external_network | default('public') }}"
47+
floating_ip_network: "{{ os_floating_network | default('public') }}"
48+
controller_params:
49+
image: hotstack-controller
50+
flavor: hotstack.small
51+
ocp_master_params:
52+
image: ipxe-boot-usb
53+
flavor: hotstack.xxlarge
54+
ironic_params:
55+
image: CentOS-Stream-GenericCloud-9
56+
cd_image: sushy-tools-blank-image
57+
flavor: hotstack.medium

0 commit comments

Comments
 (0)