Skip to content

Commit 2daf543

Browse files
authored
Make Metal3 stack persistent via quadlets (flag-controlled) (#221)
* Make Metal3 stack persistent via quadlets (flag-controlled) Add metal3_persistent flag (default: false) to deploy_ironic.yaml and deploy_bmo.yaml. When false (phase 3), the existing ephemeral podman behaviour is preserved. When true (phase 7), quadlet files are deployed to /etc/containers/systemd/ so systemd manages the Metal3 services and they survive an LZ reboot. New quadlet templates: - metal3-ironic.pod.j2 (Network=host pod) - metal3-ironic-api.container.j2 (Pull=never, conditional SSL CA) - metal3-httpd.container.j2 - metal3-bmo.container.j2 (After=metal3-ironic-api.service) New task files: - deploy_ironic_containers.yaml – ephemeral path (extracted from deploy_ironic.yaml) - deploy_ironic_quadlet_units.yaml – persistent path - deploy_bmo_container.yaml – ephemeral path (extracted from deploy_bmo.yaml) - deploy_bmo_quadlet_unit.yaml – persistent path - configure_metal3_teardown.yaml – manual decommission helper 07-configure-discovery.yaml sets metal3_persistent: true before the Metal3 deployment tasks so phase 7 uses the quadlet path. https://redhat.atlassian.net/browse/MGMT-22877 Signed-off-by: Rafa Porres Molina <rporresm@redhat.com> Assisted-by: Claude <noreply@anthropic.com> * remove unused configure_metal3_teardown.yaml Signed-off-by: Rafa Porres Molina <rporresm@redhat.com> --------- Signed-off-by: Rafa Porres Molina <rporresm@redhat.com>
1 parent cd1fe0b commit 2daf543

12 files changed

Lines changed: 466 additions & 180 deletions

playbooks/07-configure-discovery.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
- name: Load common variables
77
ansible.builtin.include_tasks: common/load-vars.yaml
88

9+
- name: Enable persistent Metal3 deployment
10+
ansible.builtin.set_fact:
11+
metal3_persistent: true
12+
913
- name: Deploy Metal3 common resources
1014
ansible.builtin.include_tasks: "../playbooks/tasks/deploy_metal3_common.yaml"
1115

playbooks/tasks/deploy_bmo.yaml

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,21 @@
1313
metal3_baremetal_operator_image: "{{ r_baremetal_operator_image.stdout }}"
1414

1515
- name: Create podman secret for kubeconfig
16+
become: "{{ metal3_persistent | default(false) | bool }}"
1617
containers.podman.podman_secret:
1718
name: metal3-kubeconfig
1819
data: "{{ lookup('file', workingDir + '/ocp-cluster/auth/kubeconfig') }}"
1920
state: present
2021
force: false
2122
no_log: true
2223

24+
- name: Pre-pull baremetal-operator image
25+
become: "{{ metal3_persistent | default(false) | bool }}"
26+
containers.podman.podman_image:
27+
name: "{{ metal3_baremetal_operator_image }}"
28+
auth_file: "{{ pullSecretPath }}"
29+
state: present
30+
2331
- name: Verify Ironic is accessible before deploying BMO
2432
ansible.builtin.uri:
2533
url: "http://{{ lzBmcIP }}:6385/v1/"
@@ -34,52 +42,16 @@
3442
until: r_ironic_check is succeeded
3543
when: metal3_bmo_wait_for_ironic | default(true) | bool
3644

37-
- name: Run baremetal-operator container
38-
containers.podman.podman_container:
39-
name: baremetal-operator
40-
image: "{{ metal3_baremetal_operator_image }}"
41-
authfile: "{{ pullSecretPath }}"
42-
user: "65532:65532"
43-
cap_drop:
44-
- ALL
45-
security_opt:
46-
- no-new-privileges
47-
secrets:
48-
- metal3-kubeconfig,type=mount,target=/kubeconfig/config,uid=65532,gid=65532,mode=0400
49-
- metal3-ironic-username,type=mount,target=/auth/ironic/username,uid=65532,gid=65532,mode=0400
50-
- metal3-ironic-password,type=mount,target=/auth/ironic/password,uid=65532,gid=65532,mode=0400
51-
env:
52-
KUBECONFIG: /kubeconfig/config
53-
IRONIC_ENDPOINT: http://{{ lzBmcIP }}:6385/v1/
54-
WATCH_NAMESPACE: infraenv
55-
LIVE_ISO_FORCE_PERSISTENT_BOOT_DEVICE: Never
56-
METAL3_AUTH_ROOT_DIR: /auth
57-
command:
58-
- /baremetal-operator
59-
- --webhook-port=0
60-
- --metrics-addr=0
61-
- --health-addr=0
62-
- --dev
63-
- --enable-leader-election=false
64-
state: started
65-
66-
- name: Wait for baremetal-operator container to be running
67-
containers.podman.podman_container_info:
68-
name: baremetal-operator
69-
register: r_bmo_ready
70-
retries: 60
71-
delay: 5
72-
until: >
73-
r_bmo_ready.containers | length > 0 and
74-
r_bmo_ready.containers[0].State.Status == 'running'
75-
changed_when: false
45+
- name: Deploy BMO container (ephemeral)
46+
when: not (metal3_persistent | default(false) | bool)
47+
ansible.builtin.include_tasks: deploy_bmo_container.yaml
7648

77-
- name: Display baremetal-operator container status
78-
containers.podman.podman_container_info:
49+
- name: Remove ephemeral baremetal-operator container before persistent deployment
50+
when: metal3_persistent | default(false) | bool
51+
containers.podman.podman_container:
7952
name: baremetal-operator
80-
register: r_bmo_status
53+
state: absent
8154

82-
- name: Show baremetal-operator status
83-
ansible.builtin.debug:
84-
msg: "Container: {{ r_bmo_status.containers[0].Name }} | State: {{ r_bmo_status.containers[0].State.Status }}"
85-
when: r_bmo_status.containers | length > 0
55+
- name: Deploy BMO via quadlet (persistent)
56+
when: metal3_persistent | default(false) | bool
57+
ansible.builtin.include_tasks: deploy_bmo_quadlet_unit.yaml
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
# BMO ephemeral container deployment (non-persistent path)
3+
4+
- name: Run baremetal-operator container
5+
containers.podman.podman_container:
6+
name: baremetal-operator
7+
image: "{{ metal3_baremetal_operator_image }}"
8+
authfile: "{{ pullSecretPath }}"
9+
user: "65532:65532"
10+
cap_drop:
11+
- ALL
12+
security_opt:
13+
- no-new-privileges
14+
secrets:
15+
- metal3-kubeconfig,type=mount,target=/kubeconfig/config,uid=65532,gid=65532,mode=0400
16+
- metal3-ironic-username,type=mount,target=/auth/ironic/username,uid=65532,gid=65532,mode=0400
17+
- metal3-ironic-password,type=mount,target=/auth/ironic/password,uid=65532,gid=65532,mode=0400
18+
env:
19+
KUBECONFIG: /kubeconfig/config
20+
IRONIC_ENDPOINT: http://{{ lzBmcIP }}:6385/v1/
21+
WATCH_NAMESPACE: infraenv
22+
LIVE_ISO_FORCE_PERSISTENT_BOOT_DEVICE: Never
23+
METAL3_AUTH_ROOT_DIR: /auth
24+
command:
25+
- /baremetal-operator
26+
- --webhook-port=0
27+
- --metrics-addr=0
28+
- --health-addr=0
29+
- --dev
30+
- --enable-leader-election=false
31+
state: started
32+
33+
- name: Wait for baremetal-operator container to be running
34+
containers.podman.podman_container_info:
35+
name: baremetal-operator
36+
register: r_bmo_ready
37+
retries: 60
38+
delay: 5
39+
until: >
40+
r_bmo_ready.containers | length > 0 and
41+
r_bmo_ready.containers[0].State.Status == 'running'
42+
changed_when: false
43+
44+
- name: Display baremetal-operator container status
45+
containers.podman.podman_container_info:
46+
name: baremetal-operator
47+
register: r_bmo_status
48+
49+
- name: Show baremetal-operator status
50+
ansible.builtin.debug:
51+
msg: "Container: {{ r_bmo_status.containers[0].Name }} | State: {{ r_bmo_status.containers[0].State.Status }}"
52+
when: r_bmo_status.containers | length > 0
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
# BMO persistent deployment via quadlet unit
3+
4+
- name: Deploy metal3-bmo container quadlet
5+
become: true
6+
ansible.builtin.template:
7+
src: "{{ playbook_dir }}/templates/quadlets/metal3-bmo.container.j2"
8+
dest: /etc/containers/systemd/metal3-bmo.container
9+
mode: '0644'
10+
register: r_bmo_quadlet
11+
12+
- name: Reload systemd daemon
13+
become: true
14+
ansible.builtin.systemd:
15+
daemon_reload: true
16+
17+
- name: Enable and start metal3-bmo service
18+
become: true
19+
ansible.builtin.systemd:
20+
name: metal3-bmo.service
21+
state: started
22+
enabled: true
23+
24+
- name: Restart metal3-bmo service if quadlet changed
25+
become: true
26+
ansible.builtin.systemd:
27+
name: metal3-bmo.service
28+
state: restarted
29+
when: r_bmo_quadlet is changed
30+
31+
- name: Wait for metal3-bmo service to be active
32+
become: true
33+
ansible.builtin.systemd:
34+
name: metal3-bmo.service
35+
register: r_bmo_svc
36+
retries: 60
37+
delay: 5
38+
until: r_bmo_svc.status.ActiveState == 'active'
39+
changed_when: false

playbooks/tasks/deploy_ironic.yaml

Lines changed: 17 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
no_log: true
3030

3131
- name: Create podman secret for ironic htpasswd
32+
become: "{{ metal3_persistent | default(false) | bool }}"
3233
containers.podman.podman_secret:
3334
name: metal3-ironic-htpasswd
3435
data: "{{ metal3_ironic_htpasswd }}"
@@ -42,6 +43,7 @@
4243

4344
- name: Create podman secret for CA bundle
4445
when: ssl_certs_configured | bool
46+
become: "{{ metal3_persistent | default(false) | bool }}"
4547
containers.podman.podman_secret:
4648
name: metal3-ca-bundle
4749
data: "{{ sslIngressCertificateFullChain + sslCACertificate | default('') }}"
@@ -50,156 +52,37 @@
5052
no_log: true
5153

5254
- name: Create ironic-conf volume
55+
become: "{{ metal3_persistent | default(false) | bool }}"
5356
containers.podman.podman_volume:
5457
name: metal3-ironic-conf
5558
state: present
5659

5760
- name: Create ironic-data volume
61+
become: "{{ metal3_persistent | default(false) | bool }}"
5862
containers.podman.podman_volume:
5963
name: metal3-ironic-data
6064
state: present
6165

6266
- name: Create ironic-shared volume
67+
become: "{{ metal3_persistent | default(false) | bool }}"
6368
containers.podman.podman_volume:
6469
name: metal3-ironic-shared
6570
state: present
6671

67-
- name: Create metal3-ironic pod
68-
containers.podman.podman_pod:
69-
name: metal3-ironic
70-
network: host
71-
share: net,ipc,uts
72-
state: started
73-
74-
- name: Wait for metal3-ironic pod infrastructure container to be ready
75-
block:
76-
- name: Get pod info to extract infra container ID
77-
containers.podman.podman_pod_info:
78-
name: metal3-ironic
79-
register: r_pod_infra_check
80-
retries: 30
81-
delay: 2
82-
until:
83-
- r_pod_infra_check.pods | length > 0
84-
- r_pod_infra_check.pods[0].InfraContainerID is defined
85-
- r_pod_infra_check.pods[0].InfraContainerID | length > 0
86-
changed_when: false
87-
88-
- name: Verify infra container is running
89-
containers.podman.podman_container_info:
90-
name: "{{ r_pod_infra_check.pods[0].InfraContainerID }}"
91-
register: r_infra_container_check
92-
retries: 10
93-
delay: 1
94-
until:
95-
- r_infra_container_check.containers | length > 0
96-
- r_infra_container_check.containers[0].State.Running | default(false)
97-
changed_when: false
98-
99-
- name: Wait for metal3-ironic pod to exist
100-
containers.podman.podman_pod_info:
101-
name: metal3-ironic
102-
register: r_pod_infra_check
103-
retries: 30
104-
delay: 2
105-
until:
106-
- r_pod_infra_check.pods | length > 0
107-
changed_when: false
108-
109-
- name: Set ironic secrets list (with CA bundle)
110-
when: ssl_certs_configured | bool
111-
ansible.builtin.set_fact:
112-
ironic_secrets:
113-
- metal3-ca-bundle,type=mount,target=/certs/ca-bundle.crt,uid=1002,gid=1003,mode=0400
114-
- metal3-ironic-htpasswd,type=env,target=IRONIC_HTPASSWD
72+
- name: Pre-pull ironic image
73+
become: "{{ metal3_persistent | default(false) | bool }}"
74+
containers.podman.podman_image:
75+
name: "{{ metal3_ironic_image }}"
76+
auth_file: "{{ pullSecretPath }}"
77+
state: present
11578

116-
- name: Set ironic secrets list (without CA bundle)
117-
when: not (ssl_certs_configured | bool)
118-
ansible.builtin.set_fact:
119-
ironic_secrets:
120-
- metal3-ironic-htpasswd,type=env,target=IRONIC_HTPASSWD
79+
- name: Deploy Ironic containers (ephemeral)
80+
when: not (metal3_persistent | default(false) | bool)
81+
ansible.builtin.include_tasks: deploy_ironic_containers.yaml
12182

122-
- name: Set ironic environment variables (with CA bundle)
123-
when: ssl_certs_configured | bool
124-
ansible.builtin.set_fact:
125-
ironic_env:
126-
IRONIC_LISTEN_PORT: "6385"
127-
HTTP_PORT: "6180"
128-
LISTEN_ALL_INTERFACES: "true"
129-
USE_IRONIC_INSPECTOR: "false"
130-
PROVISIONING_IP: "{{ lzBmcIP }}"
131-
IRONIC_USE_MARIADB: "false"
132-
IRONIC_EXPOSE_JSON_RPC: "false"
133-
OS_JSON_RPC__PORT: "6189"
134-
WEBSERVER_CACERT_FILE: /certs/ca-bundle.crt
135-
136-
- name: Set ironic environment variables (without CA bundle)
137-
when: not (ssl_certs_configured | bool)
138-
ansible.builtin.set_fact:
139-
ironic_env:
140-
IRONIC_LISTEN_PORT: "6385"
141-
HTTP_PORT: "6180"
142-
LISTEN_ALL_INTERFACES: "true"
143-
USE_IRONIC_INSPECTOR: "false"
144-
PROVISIONING_IP: "{{ lzBmcIP }}"
145-
IRONIC_USE_MARIADB: "false"
146-
IRONIC_EXPOSE_JSON_RPC: "false"
147-
OS_JSON_RPC__PORT: "6189"
148-
149-
- name: Run ironic container
150-
containers.podman.podman_container:
151-
name: ironic
152-
pod: metal3-ironic
153-
image: "{{ metal3_ironic_image }}"
154-
authfile: "{{ pullSecretPath }}"
155-
restart_policy: always
156-
user: "1002:1003"
157-
cap_drop:
158-
- ALL
159-
volume:
160-
- metal3-ironic-conf:/conf
161-
- metal3-ironic-data:/data
162-
- metal3-ironic-shared:/shared
163-
secrets: "{{ ironic_secrets }}"
164-
env: "{{ ironic_env }}"
165-
command:
166-
- /bin/runironic
167-
state: started
168-
169-
- name: Run httpd container
170-
containers.podman.podman_container:
171-
name: httpd
172-
pod: metal3-ironic
173-
image: "{{ metal3_ironic_image }}"
174-
authfile: "{{ pullSecretPath }}"
175-
restart_policy: always
176-
user: "1002:1003"
177-
cap_drop:
178-
- ALL
179-
volume:
180-
- metal3-ironic-shared:/shared
181-
env:
182-
IRONIC_LISTEN_PORT: "6385"
183-
HTTP_PORT: "6180"
184-
LISTEN_ALL_INTERFACES: "true"
185-
USE_IRONIC_INSPECTOR: "false"
186-
PROVISIONING_IP: "{{ lzBmcIP }}"
187-
command:
188-
- /bin/runhttpd
189-
state: started
190-
191-
- name: Wait for metal3-ironic pod and containers to be running
192-
containers.podman.podman_pod_info:
193-
name: metal3-ironic
194-
register: r_pod_ready
195-
retries: 60
196-
delay: 5
197-
until:
198-
- r_pod_ready.pods | length > 0
199-
- r_pod_ready.pods[0].Containers | selectattr('Name', 'equalto', 'ironic') | selectattr('State', 'equalto', 'running') | list | length == 1
200-
- r_pod_ready.pods[0].Containers | selectattr('Name', 'equalto', 'httpd') | selectattr('State', 'equalto', 'running') | list | length == 1
201-
changed_when: false
202-
# Note: Checks that both ironic and httpd containers are running (infra container state is ignored)
83+
- name: Deploy Ironic via quadlet (persistent)
84+
when: metal3_persistent | default(false) | bool
85+
ansible.builtin.include_tasks: deploy_ironic_quadlet_units.yaml
20386

20487
- name: Verify HTTP server accessibility
20588
ansible.builtin.uri:

0 commit comments

Comments
 (0)