@@ -163,6 +163,213 @@ When bonding is configured, the `ctlplaneInterface` should be set to the bond in
163163(e.g., `bond0`), and the physical interfaces specified in `bondInterfaces` will be configured
164164as members of the bond during node provisioning.
165165
166+ == Using custom OS Images with Provision Server
167+
168+ This assumes you have already built a qcow2 image with all the required packages for EDPM
169+ deployment. For instructions on building custom images, refer to the
170+ https://github.com/openstack-k8s-operators/edpm-image-builder[edpm-image-builder].
171+
172+ By default, OpenStackBaremetalSet automatically creates an `OpenStackProvisionServer` per
173+ nodeset to serve the bundled OS image for node provisioning. You can also use a custom provision
174+ server with a different OS image.
175+
176+ To use a custom OS image, you must first package your qcow2 image as a container image.
177+ The packaging process is the same for all approaches described below.
178+
179+ === Packaging Your Custom qcow2 Image
180+
181+ ==== Container Image Requirements
182+
183+ The container image used for `osContainerImageUrl` must:
184+
185+ . Contain the qcow2 disk image file and its checksum file
186+ . Have an entrypoint script (like `copy_out.sh`) that copies the qcow2 file to the directory
187+ specified by the `DEST_DIR` environment variable
188+ . Exit successfully after copying the file (it runs as an init container)
189+
190+ ==== Building a Container Image from an Existing qcow2
191+
192+ If you have an existing qcow2 image, you can package it using the `copy_out.sh` script from
193+ the https://github.com/openstack-k8s-operators/edpm-image-builder[edpm-image-builder]
194+ repository.
195+
196+ ===== Step 1: Generate Checksum File
197+
198+ *Required:* Create a checksum file for your qcow2 image. The provision server requires a
199+ checksum file to function properly - the checksum discovery agent will fail if no checksum
200+ file is found. The provision server supports MD5, SHA256, or SHA512 checksums. The checksum
201+ file must contain the hash type in its filename (e.g., `md5`, `sha256`, or `sha512`):
202+
203+ [,sh]
204+ ----
205+ # For SHA256 (recommended)
206+ sha256sum my-custom-image.qcow2 > my-custom-image.qcow2.sha256sum
207+
208+ # Or for MD5
209+ md5sum my-custom-image.qcow2 > my-custom-image.qcow2.md5sum
210+
211+ # Or for SHA512
212+ sha512sum my-custom-image.qcow2 > my-custom-image.qcow2.sha512sum
213+ ----
214+
215+ ===== Step 2: Clone the Repository
216+
217+ Clone the edpm-image-builder repository to get the `copy_out.sh` script:
218+
219+ [,sh]
220+ ----
221+ git clone https://github.com/openstack-k8s-operators/edpm-image-builder.git
222+ cd edpm-image-builder
223+ ----
224+
225+ ===== Step 3: Create Containerfile
226+
227+ Create a `Containerfile` (or `Dockerfile`) in the same directory. Copy both your qcow2 image
228+ and its checksum file (checksum is required):
229+
230+ [,dockerfile]
231+ ----
232+ FROM registry.access.redhat.com/ubi9/ubi-minimal:9.6
233+
234+ # Copy your qcow2 image and checksum file into the container
235+ # The copy_out.sh script expects files in the root directory (/) by default
236+ COPY my-custom-image.qcow2 /
237+ COPY my-custom-image.qcow2.sha256sum /
238+
239+ # Copy the copy_out.sh script from the repository
240+ COPY copy_out.sh /copy_out.sh
241+ RUN chmod +x /copy_out.sh
242+
243+ ENTRYPOINT ["/copy_out.sh"]
244+ ----
245+
246+ *Note:*
247+
248+ * Replace `my-custom-image.qcow2` with your actual qcow2 filename
249+ * Replace `my-custom-image.qcow2.sha256sum` with your checksum filename (must contain `md5`,
250+ `sha256`, or `sha512` in the filename to be detected by the provision server)
251+ * The files are copied to `/` (root directory) because `copy_out.sh` expects to find them
252+ there by default (the default `SRC_DIR` is `/`). You can set `ENV SRC_DIR=<path>` in your
253+ Containerfile if you want to use a different source directory.
254+ * The `copy_out.sh` script handles both compressed (`.qcow2.gz`) and uncompressed (`.qcow2`)
255+ images
256+
257+ ===== Step 4: Build and Push
258+
259+ Build and push the container image:
260+
261+ [,sh]
262+ ----
263+ buildah bud -f Containerfile -t <your-registry>/my-custom-os-image:latest
264+ buildah push <your-registry>/my-custom-os-image:latest
265+ ----
266+
267+ Or using podman/docker:
268+
269+ [,sh]
270+ ----
271+ podman build -f Containerfile -t <your-registry>/my-custom-os-image:latest
272+ podman push <your-registry>/my-custom-os-image:latest
273+ ----
274+
275+ === Using the Custom OS Image
276+
277+ After packaging your custom qcow2 image as a container image, you can use it with one of the
278+ following approaches:
279+
280+ ==== Using OpenStackVersion CR (Recommended)
281+
282+ To use a custom OS image across multiple nodesets while maintaining centralized version
283+ management, you can patch the `OpenStackVersion` CR with the custom image in
284+ `customContainerImages`. This allows all nodesets to use the same custom image without
285+ specifying the container image URL individually in each nodeset.
286+
287+ *Tip:* If you name your qcow2 image `edpm-hardened-uefi.qcow2` (the default osImage name)
288+ , you can avoid having to specify the `osImage` field in every nodeset.
289+
290+ Otherwise, you will need to specify the `osImage` field with your custom image name in each
291+ nodeset.
292+
293+ Patch the `OpenStackVersion` CR:
294+
295+ [,console]
296+ ----
297+ oc patch openstackversion openstack --type='merge' -p='
298+ spec:
299+ customContainerImages:
300+ osContainerImage: <your-registry>/my-custom-os-image:latest
301+ '
302+ ----
303+
304+ ==== Using osContainerImageUrl in baremetalSetTemplate
305+
306+ It is also possible to specify `osContainerImageUrl` directly in the `baremetalSetTemplate`
307+ of your `OpenStackDataPlaneNodeSet`, this approach requires updating each nodeset individually.
308+
309+ [,yaml]
310+ ----
311+ apiVersion: dataplane.openstack.org/v1beta1
312+ kind: OpenStackDataPlaneNodeSet
313+ metadata:
314+ name: openstack-edpm
315+ spec:
316+ baremetalSetTemplate:
317+ bmhLabelSelector:
318+ app: openstack
319+ workload: compute
320+ ctlplaneInterface: enp1s0
321+ cloudUserName: cloud-admin
322+ osImage: my-custom-image.qcow2
323+ osContainerImageUrl: <your-registry>/my-custom-os-image:latest
324+ nodes:
325+ edpm-compute-0:
326+ hostName: edpm-compute-0
327+ ----
328+
329+ ==== Creating a Custom OpenStackProvisionServer
330+
331+ You can also create a dedicated `OpenStackProvisionServer` resource. Note that this requires
332+ specifying the provision server in each nodeset. This approach allows you to use the same
333+ provision server for multiple nodesets and avoid exhausting available host ports, since each
334+ auto-created provision server uses a host port.
335+
336+ After building and pushing your container image (as described in the packaging section above),
337+ create the `OpenStackProvisionServer` resource:
338+
339+ [,yaml]
340+ ----
341+ apiVersion: baremetal.openstack.org/v1beta1
342+ kind: OpenStackProvisionServer
343+ metadata:
344+ name: openstackprovisionserver
345+ spec:
346+ interface: enp1s0
347+ port: 6190
348+ osImage: my-custom-image.qcow2 # qcow2 file inside the container
349+ osContainerImageUrl: <your-registry>/my-custom-os-image:latest
350+ apacheImageUrl: registry.redhat.io/ubi9/httpd-24:latest
351+ agentImageUrl: quay.io/openstack-k8s-operators/openstack-baremetal-operator-agent:latest
352+ ----
353+
354+ Then reference this provision server in your `OpenStackDataPlaneNodeSet`:
355+
356+ [,yaml]
357+ ----
358+ apiVersion: dataplane.openstack.org/v1beta1
359+ kind: OpenStackDataPlaneNodeSet
360+ metadata:
361+ name: example-nodeset
362+ spec:
363+ baremetalSetTemplate:
364+ provisionServerName: openstackprovisionserver
365+ osImage: my-custom-image.qcow2
366+ deploymentSSHSecret: custom-ssh-secret
367+ ctlplaneInterface: enp1s0
368+ nodes:
369+ edpm-compute-0:
370+ hostName: edpm-compute-0
371+ ----
372+
166373=== Relevant Status Condition
167374
168375`NodeSetBaremetalProvisionReady` condition in status condtions reflects the status of
0 commit comments