From 6c057269476c2ee06d4732e10ce73b4172b168789308822a4a0f484d9d6edbe6 Mon Sep 17 00:00:00 2001 From: Nicolas Belouin Date: Thu, 31 Jul 2025 13:26:06 +0200 Subject: [PATCH 1/3] Port ironic-image and ipa-downloader-image PRs to Factory, and adapt chart accordingly Signed-off-by: Nicolas Belouin --- ironic-image/Dockerfile | 3 +- ironic-image/ironic-config/ironic.conf.j2 | 20 ++++---- ironic-image/prepare-efi.sh | 2 +- ironic-image/scripts/configure-ironic.sh | 46 +++++++++++++++++-- ironic-image/scripts/runhttpd | 1 - ironic-ipa-downloader-image/get-resource.sh | 15 +++--- .../templates/configmap-ironic.yaml | 4 -- .../charts/ironic/templates/configmap.yaml | 3 -- metal3-chart/values.yaml | 3 -- 9 files changed, 62 insertions(+), 35 deletions(-) diff --git a/ironic-image/Dockerfile b/ironic-image/Dockerfile index 9912dfa..a5fa085 100644 --- a/ironic-image/Dockerfile +++ b/ironic-image/Dockerfile @@ -88,8 +88,7 @@ RUN if [ "$(uname -m)" = "aarch64" ]; then\ cp /usr/share/ipxe/snp-arm64.efi /tftpboot/ipxe.efi; cp /usr/share/ipxe/snp-arm64.efi /tftpboot/snp-arm64.efi; cp /usr/share/ipxe/snp-arm64.efi /tftpboot/snp.efi ;\ fi -COPY --from=base /tmp/esp-x86_64.img /tmp/uefi_esp-x86_64.img -COPY --from=base /tmp/esp-aarch64.img /tmp/uefi_esp-arm64.img +COPY --from=base /tmp/uefi_esp_*.img /templates/ COPY ironic-config/ironic.conf.j2 ironic-config/network-data-schema-empty.json /etc/ironic/ diff --git a/ironic-image/ironic-config/ironic.conf.j2 b/ironic-image/ironic-config/ironic.conf.j2 index 3c7ea0e..3e4b29c 100644 --- a/ironic-image/ironic-config/ironic.conf.j2 +++ b/ironic-image/ironic-config/ironic.conf.j2 @@ -91,21 +91,23 @@ send_sensor_data = {{ env.SEND_SENSOR_DATA }} # Power state is checked every 60 seconds and BMC activity should # be avoided more often than once every sixty seconds. send_sensor_data_interval = 160 -{% if env.VMEDIA_TLS_PORT %} -bootloader = {{ env.IRONIC_HTTPS_VMEDIA_URL }}/uefi_esp-{{ env.DEPLOY_ARCHITECTURE }}.img -{% else %} -bootloader = {{ env.IRONIC_HTTP_URL }}/uefi_esp-{{ env.DEPLOY_ARCHITECTURE }}.img -{% endif %} +bootloader_by_arch = {{ env.BOOTLOADER_BY_ARCH }} verify_step_priority_override = management.clear_job_queue:90 # We don't use this feature, and it creates an additional load on the database node_history = False # Provide for a timeout longer than 60 seconds for certain vendor's hardware power_state_change_timeout = 120 -{% if env.IRONIC_DEFAULT_KERNEL is defined %} -deploy_kernel = file://{{ env.IRONIC_DEFAULT_KERNEL }} +{% if env.DEPLOY_KERNEL_URL is defined %} +deploy_kernel = {{ env.DEPLOY_KERNEL_URL }} {% endif %} -{% if env.IRONIC_DEFAULT_RAMDISK is defined %} -deploy_ramdisk = file://{{ env.IRONIC_DEFAULT_RAMDISK }} +{% if env.DEPLOY_KERNEL_BY_ARCH is defined %} +deploy_kernel_by_arch = {{ env.DEPLOY_KERNEL_BY_ARCH }} +{% endif %} +{% if env.DEPLOY_RAMDISK_URL is defined %} +deploy_ramdisk = {{ env.DEPLOY_RAMDISK_URL }} +{% endif %} +{% if env.DEPLOY_RAMDISK_BY_ARCH is defined %} +deploy_ramdisk_by_arch = {{ env.DEPLOY_RAMDISK_BY_ARCH }} {% endif %} {% if env.DISABLE_DEEP_IMAGE_INSPECTION | lower == "true" %} disable_deep_image_inspection = True diff --git a/ironic-image/prepare-efi.sh b/ironic-image/prepare-efi.sh index d4b2f2e..0c57a35 100644 --- a/ironic-image/prepare-efi.sh +++ b/ironic-image/prepare-efi.sh @@ -9,7 +9,7 @@ declare -A efi_arch=( for arch in "${!efi_arch[@]}"; do - DEST=/tmp/esp-${arch}.img + DEST=/tmp/uefi_esp_${arch}.img dd bs=1024 count=6400 if=/dev/zero of=$DEST mkfs.msdos -F 12 -n 'ESP_IMAGE' $DEST diff --git a/ironic-image/scripts/configure-ironic.sh b/ironic-image/scripts/configure-ironic.sh index 149812f..508f4d0 100755 --- a/ironic-image/scripts/configure-ironic.sh +++ b/ironic-image/scripts/configure-ironic.sh @@ -76,10 +76,41 @@ if [[ -n "$IRONIC_EXTERNAL_IP" ]]; then fi fi -IMAGE_CACHE_PREFIX="/shared/html/images/ironic-python-agent-${DEPLOY_ARCHITECTURE}" -if [[ -f "${IMAGE_CACHE_PREFIX}.kernel" ]] && [[ -f "${IMAGE_CACHE_PREFIX}.initramfs" ]]; then - export IRONIC_DEFAULT_KERNEL="${IMAGE_CACHE_PREFIX}.kernel" - export IRONIC_DEFAULT_RAMDISK="${IMAGE_CACHE_PREFIX}.initramfs" +IMAGE_CACHE_PREFIX="/shared/html/images/ironic-python-agent" +if [[ -z "${DEPLOY_KERNEL_URL:-}" ]] && [[ -z "${DEPLOY_RAMDISK_URL:-}" ]] && \ + [[ -f "${IMAGE_CACHE_PREFIX}.kernel" ]] && [[ -f "${IMAGE_CACHE_PREFIX}.initramfs" ]]; then + export DEPLOY_KERNEL_URL="file://${IMAGE_CACHE_PREFIX}.kernel" + export DEPLOY_RAMDISK_URL="file://${IMAGE_CACHE_PREFIX}.initramfs" +fi + +declare -A detected_arch +for var_arch in "${!DEPLOY_KERNEL_URL_@}"; do + IPA_ARCH="${var_arch#DEPLOY_KERNEL_URL}" + detected_arch["${IPA_ARCH,,}"]=1 +done +for file_arch in "${IMAGE_CACHE_PREFIX}"_*.kernel; do + if [[ -f "${file_arch}" ]]; then + IPA_ARCH="$(basename "${file_arch#"${IMAGE_CACHE_PREFIX}"_}" .kernel)" + detected_arch["${IPA_ARCH}"]=1 + fi +done + +DEPLOY_KERNEL_BY_ARCH="" +DEPLOY_RAMDISK_BY_ARCH="" +for IPA_ARCH in "${!detected_arch[@]}"; do + kernel_var="DEPLOY_KERNEL_URL_${IPA_ARCH^^}" + ramdisk_var="DEPLOY_RAMDISK_URL_${IPA_ARCH^^}" + if [[ -z "${!kernel_var:-}" ]] && [[ -z "${!ramdisk_var:-}" ]] && \ + [[ -f "${IMAGE_CACHE_PREFIX}_${IPA_ARCH}.kernel" ]] && [[ -f "${IMAGE_CACHE_PREFIX}_${IPA_ARCH}.initramfs" ]]; then + export "${kernel_var}"="file://${IMAGE_CACHE_PREFIX}_${IPA_ARCH}.kernel" + export "${ramdisk_var}"="file://${IMAGE_CACHE_PREFIX}_${IPA_ARCH}.initramfs" + fi + DEPLOY_KERNEL_BY_ARCH+="${!kernel_var:+${IPA_ARCH}:${!kernel_var},}" + DEPLOY_RAMDISK_BY_ARCH+="${!ramdisk_var:+${IPA_ARCH}:${!ramdisk_var},}" +done +if [[ -n "${DEPLOY_KERNEL_BY_ARCH}" ]] && [[ -n "${DEPLOY_RAMDISK_BY_ARCH}" ]]; then + export DEPLOY_KERNEL_BY_ARCH="${DEPLOY_KERNEL_BY_ARCH%?}" + export DEPLOY_RAMDISK_BY_ARCH="${DEPLOY_RAMDISK_BY_ARCH%?}" fi if [[ -f "${IRONIC_CONF_DIR}/ironic.conf" ]]; then @@ -87,6 +118,13 @@ if [[ -f "${IRONIC_CONF_DIR}/ironic.conf" ]]; then cp "${IRONIC_CONF_DIR}/ironic.conf" "${IRONIC_CONF_DIR}/ironic.conf.orig" fi +BOOTLOADER_BY_ARCH="" +for bootloader in /templates/uefi_esp_*.img; do + BOOTLOADER_ARCH="$(basename "${bootloader#/templates/uefi_esp_}" .img)" + BOOTLOADER_BY_ARCH+="${BOOTLOADER_ARCH}:file://${bootloader}," +done +export BOOTLOADER_BY_ARCH="${BOOTLOADER_BY_ARCH%?}" + # oslo.config also supports Config Opts From Environment, log them to stdout echo 'Options set from Environment variables' env | grep "^OS_" || true diff --git a/ironic-image/scripts/runhttpd b/ironic-image/scripts/runhttpd index 97208cd..c094689 100755 --- a/ironic-image/scripts/runhttpd +++ b/ironic-image/scripts/runhttpd @@ -37,7 +37,6 @@ export INSPECTOR_EXTRA_ARGS # Copy files to shared mount render_j2_config /tmp/inspector.ipxe.j2 /shared/html/inspector.ipxe -cp /tmp/uefi_esp*.img /shared/html/ # cp -r /etc/httpd/* "${HTTPD_DIR}" if [[ -f "${HTTPD_CONF_DIR}/httpd.conf" ]]; then mv "${HTTPD_CONF_DIR}/httpd.conf" "${HTTPD_CONF_DIR}/httpd.conf.example" diff --git a/ironic-ipa-downloader-image/get-resource.sh b/ironic-ipa-downloader-image/get-resource.sh index 14e9293..f3d1c66 100644 --- a/ironic-ipa-downloader-image/get-resource.sh +++ b/ironic-ipa-downloader-image/get-resource.sh @@ -29,13 +29,12 @@ if [ -z "${IPA_BASEURI}" ]; then # SLES BASED IPA - ironic-ipa-ramdisk-x86_64 and ironic-ipa-ramdisk-aarch64 packages mkdir -p /shared/html/images if [ -f ${IMAGES_BASE_PATH}/initrd-x86_64.zst ]; then - cp ${IMAGES_BASE_PATH}/initrd-x86_64.zst /shared/html/images/ironic-python-agent-x86_64.initramfs - cp ${IMAGES_BASE_PATH}/openstack-ironic-image.x86_64*.kernel /shared/html/images/ironic-python-agent-x86_64.kernel + cp ${IMAGES_BASE_PATH}/initrd-x86_64.zst /shared/html/images/ironic-python-agent_x86_64.initramfs + cp ${IMAGES_BASE_PATH}/openstack-ironic-image.x86_64*.kernel /shared/html/images/ironic-python-agent_x86_64.kernel fi - # Use arm64 as destination for iPXE compatibility if [ -f ${IMAGES_BASE_PATH}/initrd-aarch64.zst ]; then - cp ${IMAGES_BASE_PATH}/initrd-aarch64.zst /shared/html/images/ironic-python-agent-arm64.initramfs - cp ${IMAGES_BASE_PATH}/openstack-ironic-image.aarch64*.kernel /shared/html/images/ironic-python-agent-arm64.kernel + cp ${IMAGES_BASE_PATH}/initrd-aarch64.zst /shared/html/images/ironic-python-agent_aarch64.initramfs + cp ${IMAGES_BASE_PATH}/openstack-ironic-image.aarch64*.kernel /shared/html/images/ironic-python-agent_aarch64.kernel fi cp /tmp/images.sha256 /shared/images.sha256 @@ -87,8 +86,8 @@ else chmod 755 "$TMPDIR" mv "$TMPDIR" "$FILENAME-$ETAG" ln -sf "$FILENAME-$ETAG/$FFILENAME.headers" "$FFILENAME.headers" - ln -sf "$FILENAME-$ETAG/$FILENAME.initramfs" "$FILENAME-${ARCH,,}.initramfs" - ln -sf "$FILENAME-$ETAG/$FILENAME.kernel" "$FILENAME-${ARCH,,}.kernel" + ln -sf "$FILENAME-$ETAG/$FILENAME.initramfs" "${FILENAME}_${ARCH,,}.initramfs" + ln -sf "$FILENAME-$ETAG/$FILENAME.kernel" "${FILENAME}_${ARCH,,}.kernel" IMAGE_CHANGED=1 else @@ -100,7 +99,7 @@ if [ "${CERTS_CHANGED:-0}" = "1" ] || [ "${IMAGE_CHANGED:-0}" = "1" ]; then mkdir -p /tmp/ca/tmp-initrd && cd /tmp/ca/tmp-initrd mkdir -p etc/ironic-python-agent.d/ca-certs cp /tmp/ironic-certificates/* etc/ironic-python-agent.d/ca-certs/ - for initramfs in /shared/html/images/ironic-python-agent-*.initramfs; do + for initramfs in /shared/html/images/ironic-python-agent_*.initramfs; do find . | cpio -o -H newc --reproducible | zstd -c >> "${initramfs}" done cp /tmp/certificates.sha256 /shared/certificates.sha256 diff --git a/metal3-chart/charts/baremetal-operator/templates/configmap-ironic.yaml b/metal3-chart/charts/baremetal-operator/templates/configmap-ironic.yaml index bab310c..31ea579 100644 --- a/metal3-chart/charts/baremetal-operator/templates/configmap-ironic.yaml +++ b/metal3-chart/charts/baremetal-operator/templates/configmap-ironic.yaml @@ -5,7 +5,6 @@ {{- $ironicApiHost := print $ironicHost ":6385" }} {{- $ironicBootHost := print $ironicHost ":6180" }} {{- $ironicCacheHost := print $ironicHost ":6180" }} - {{- $deployArch := .Values.global.deployArchitecture }} apiVersion: v1 data: @@ -21,9 +20,6 @@ data: RESTART_CONTAINER_CERTIFICATE_UPDATED: "false" {{- end }} CACHEURL: "{{ $protocol }}://{{ $ironicCacheHost }}/images" - DEPLOY_KERNEL_URL: "{{ $protocol }}://{{ $ironicBootHost }}/images/ironic-python-agent-{{ $deployArch }}.kernel" - DEPLOY_RAMDISK_URL: "{{ $protocol }}://{{ $ironicBootHost }}/images/ironic-python-agent-{{ $deployArch }}.initramfs" - DEPLOY_ARCHITECTURE: "{{ $deployArch }}" {{- if .Values.baremetaloperator.externalHttpIPv6 }} {{- $port := ternary .Values.global.vmediaTLSPort .Values.baremetaloperator.httpPort $enableVMediaTLS }} IRONIC_EXTERNAL_URL_V6: "{{ $protocol }}://[{{ .Values.baremetaloperator.externalHttpIPv6 }}]:{{ $port }}" diff --git a/metal3-chart/charts/ironic/templates/configmap.yaml b/metal3-chart/charts/ironic/templates/configmap.yaml index 7262e58..f46830b 100644 --- a/metal3-chart/charts/ironic/templates/configmap.yaml +++ b/metal3-chart/charts/ironic/templates/configmap.yaml @@ -5,8 +5,6 @@ metadata: labels: {{- include "ironic.labels" . | nindent 4 }} data: - {{- $deployArch := .Values.global.deployArchitecture }} - {{- if ( .Values.global.enable_dnsmasq ) }} DNSMASQ_DNS_SERVER_ADDRESS: {{ .Values.global.dnsmasqDNSServer }} DNSMASQ_DEFAULT_ROUTER: {{ .Values.global.dnsmasqDefaultRouter }} @@ -18,7 +16,6 @@ data: HTTP_PORT: "6180" PREDICTABLE_NIC_NAMES: "{{ .Values.global.predictableNicNames }}" IRONIC_EXTERNAL_HTTP_URL: {{ include "ironic.externalHttpUrl" . }} - DEPLOY_ARCHITECTURE: {{ $deployArch }} ENABLE_PXE_BOOT: "{{ .Values.global.enable_pxe_boot }}" {{- if .Values.global.provisioningInterface }} PROVISIONING_INTERFACE: {{ .Values.global.provisioningInterface }} diff --git a/metal3-chart/values.yaml b/metal3-chart/values.yaml index d9f62a0..2364916 100644 --- a/metal3-chart/values.yaml +++ b/metal3-chart/values.yaml @@ -72,9 +72,6 @@ global: # Name for the MariaDB service databaseServiceName: metal3-mariadb - # Architecture for deployed nodes (either x86_64 or arm64) - deployArchitecture: x86_64 - # In a multi-node cluster use the node selector to ensure the pods # all run on the same host where the dnsmasqDNSServer and provisioningIP # and /opt/media exist. Uncomment the nodeSelector and update the -- 2.49.0 From bb4ab90787fa9afa89fd22d732aad6f48110eb10a81ff19a80423901f2666f79 Mon Sep 17 00:00:00 2001 From: Nicolas Belouin Date: Thu, 31 Jul 2025 13:55:05 +0200 Subject: [PATCH 2/3] Add multi-arch related patches for BMO Signed-off-by: Nicolas Belouin --- .../0001-Enable-exhaustive-linter.patch | 163 +++++++ ...Stop-requiring-DEPLOY_KERNEL-RAMDISK.patch | 91 ++++ ...RNEL_URL-from-deployment-scripts-for.patch | 49 ++ ...or-setting-various-Ironic-properties.patch | 422 ++++++++++++++++++ ...ne-docs-for-node-configuration-calls.patch | 46 ++ baremetal-operator/baremetal-operator.spec | 7 + 6 files changed, 778 insertions(+) create mode 100644 baremetal-operator/0001-Enable-exhaustive-linter.patch create mode 100644 baremetal-operator/0002-Stop-requiring-DEPLOY_KERNEL-RAMDISK.patch create mode 100644 baremetal-operator/0003-Remove-DEPLOY_KERNEL_URL-from-deployment-scripts-for.patch create mode 100644 baremetal-operator/0004-Refactor-setting-various-Ironic-properties.patch create mode 100644 baremetal-operator/0005-Provide-inline-docs-for-node-configuration-calls.patch diff --git a/baremetal-operator/0001-Enable-exhaustive-linter.patch b/baremetal-operator/0001-Enable-exhaustive-linter.patch new file mode 100644 index 0000000..fc24436 --- /dev/null +++ b/baremetal-operator/0001-Enable-exhaustive-linter.patch @@ -0,0 +1,163 @@ +From f8c1ba1696fd8555e8e94246ec5afa38536fa8bd Mon Sep 17 00:00:00 2001 +From: erjavaskivuori +Date: Thu, 5 Jun 2025 09:49:47 +0000 +Subject: [PATCH 1/5] Enable exhaustive linter + +Enable exhaustive linter to check exhaustiveness of switch statements of enum-like +constants. + +Signed-off-by: erjavaskivuori +(cherry picked from commit a5a81b8717c9e6642ae626ea97933e3615fe11c0) +--- + .golangci.yaml | 4 ++- + .../metal3.io/v1alpha1/baremetalhost_types.go | 1 + + .../metal3.io/baremetalhost_controller.go | 2 ++ + .../metal3.io/host_state_machine.go | 4 +++ + pkg/provisioner/ironic/ironic.go | 26 +++++++++---------- + 5 files changed, 22 insertions(+), 15 deletions(-) + +diff --git a/.golangci.yaml b/.golangci.yaml +index 58e54b31..c758b93c 100644 +--- a/.golangci.yaml ++++ b/.golangci.yaml +@@ -21,7 +21,7 @@ linters: + - errchkjson + #- errname + #- errorlint +- #- exhaustive ++ - exhaustive + - exptostd + - fatcontext + #- forbidigo +@@ -78,6 +78,8 @@ linters: + # Run with --fast=false for more extensive checks + fast: true + linters-settings: ++ exhaustive: ++ default-signifies-exhaustive: true + gosec: + severity: medium + confidence: medium +diff --git a/apis/metal3.io/v1alpha1/baremetalhost_types.go b/apis/metal3.io/v1alpha1/baremetalhost_types.go +index ba1b4333..426a7a89 100644 +--- a/apis/metal3.io/v1alpha1/baremetalhost_types.go ++++ b/apis/metal3.io/v1alpha1/baremetalhost_types.go +@@ -1113,6 +1113,7 @@ func (host *BareMetalHost) OperationMetricForState(operation ProvisioningState) + metric = &history.Provision + case StateDeprovisioning: + metric = &history.Deprovision ++ default: + } + return + } +diff --git a/internal/controller/metal3.io/baremetalhost_controller.go b/internal/controller/metal3.io/baremetalhost_controller.go +index 33310bf7..1998627e 100644 +--- a/internal/controller/metal3.io/baremetalhost_controller.go ++++ b/internal/controller/metal3.io/baremetalhost_controller.go +@@ -586,6 +586,7 @@ func getCurrentImage(host *metal3api.BareMetalHost) *metal3api.Image { + if host.Spec.Image != nil && host.Spec.Image.URL != "" { + return host.Spec.Image.DeepCopy() + } ++ default: + } + return nil + } +@@ -816,6 +817,7 @@ func (r *BareMetalHostReconciler) registerHost(prov provisioner.Provisioner, inf + if info.host.Spec.AutomatedCleaningMode == metal3api.CleaningModeDisabled { + preprovImgFormats = nil + } ++ default: + } + + preprovImg, err := r.getPreprovImage(info, preprovImgFormats) +diff --git a/internal/controller/metal3.io/host_state_machine.go b/internal/controller/metal3.io/host_state_machine.go +index 8b382553..6d88591b 100644 +--- a/internal/controller/metal3.io/host_state_machine.go ++++ b/internal/controller/metal3.io/host_state_machine.go +@@ -107,6 +107,7 @@ func (hsm *hostStateMachine) updateHostStateFrom(initialState metal3api.Provisio + if actionRes := hsm.ensureCapacity(info, hsm.NextState); actionRes != nil { + return actionRes + } ++ default: + } + + info.log.Info("changing provisioning state", +@@ -137,6 +138,7 @@ func (hsm *hostStateMachine) updateHostStateFrom(initialState metal3api.Provisio + info.log.Info("saving boot mode", + "new mode", hsm.Host.Status.Provisioning.BootMode) + } ++ default: + } + } + +@@ -163,6 +165,7 @@ func (hsm *hostStateMachine) checkDelayedHost(info *reconcileInfo) actionResult + if actionRes := hsm.ensureCapacity(info, info.host.Status.Provisioning.State); actionRes != nil { + return actionRes + } ++ default: + } + + return nil +@@ -299,6 +302,7 @@ func (hsm *hostStateMachine) checkDetachedHost(info *reconcileInfo) (result acti + switch info.host.Status.Provisioning.State { + case metal3api.StateProvisioned, metal3api.StateExternallyProvisioned, metal3api.StateReady, metal3api.StateAvailable: + return hsm.Reconciler.detachHost(hsm.Provisioner, info) ++ default: + } + } + if info.host.Status.ErrorType == metal3api.DetachError { +diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go +index 9a4b4589..4c4923ad 100644 +--- a/pkg/provisioner/ironic/ironic.go ++++ b/pkg/provisioner/ironic/ironic.go +@@ -335,21 +335,17 @@ func (p *ironicProvisioner) configureImages(data provisioner.ManagementAccessDat + return result, err + } + ++ if data.State == metal3api.StateProvisioning && data.CurrentImage.IsLiveISO() { ++ // Live ISO doesn't need pre-provisioning image ++ return result, nil ++ } ++ ++ if data.State == metal3api.StateDeprovisioning && data.AutomatedCleaningMode == metal3api.CleaningModeDisabled { ++ // No need for pre-provisioning image if cleaning disabled ++ return result, nil ++ } ++ + switch data.State { +- case metal3api.StateProvisioning, +- metal3api.StateDeprovisioning: +- if data.State == metal3api.StateProvisioning { +- if data.CurrentImage.IsLiveISO() { +- // Live ISO doesn't need pre-provisioning image +- return result, nil +- } +- } else { +- if data.AutomatedCleaningMode == metal3api.CleaningModeDisabled { +- // No need for pre-provisioning image if cleaning disabled +- return result, nil +- } +- } +- fallthrough + case metal3api.StateInspecting, + metal3api.StatePreparing: + if deployImageInfo == nil { +@@ -360,6 +356,7 @@ func (p *ironicProvisioner) configureImages(data provisioner.ManagementAccessDat + } + return result, err + } ++ default: + } + + return result, nil +@@ -1724,6 +1721,7 @@ func (p *ironicProvisioner) loadBusyHosts() (hosts map[string]struct{}, err erro + if !strings.Contains(node.BootInterface, "virtual-media") { + hosts[node.Name] = struct{}{} + } ++ default: + } + } + +-- +2.50.1 + diff --git a/baremetal-operator/0002-Stop-requiring-DEPLOY_KERNEL-RAMDISK.patch b/baremetal-operator/0002-Stop-requiring-DEPLOY_KERNEL-RAMDISK.patch new file mode 100644 index 0000000..e93c2e0 --- /dev/null +++ b/baremetal-operator/0002-Stop-requiring-DEPLOY_KERNEL-RAMDISK.patch @@ -0,0 +1,91 @@ +From 509ba92a8ed7303a418c5277f7544db2765c3802 Mon Sep 17 00:00:00 2001 +From: Dmitry Tantsur +Date: Wed, 2 Jul 2025 17:33:46 +0200 +Subject: [PATCH 2/5] Stop requiring DEPLOY_KERNEL/RAMDISK + +Ironic has global configuration that allows specifying them, even +depending on the architecture. Our ironic-image supports that when +IPA downloader is used (and should start supporting explicit variables +too). + +Signed-off-by: Dmitry Tantsur +(cherry picked from commit 0f1ef6cbeb8815f19d853ba5eab1e70c7d85e2ec) +--- + pkg/provisioner/ironic/factory.go | 6 ++---- + pkg/provisioner/ironic/factory_test.go | 9 ++------- + pkg/provisioner/ironic/ironic.go | 10 +++------- + 3 files changed, 7 insertions(+), 18 deletions(-) + +diff --git a/pkg/provisioner/ironic/factory.go b/pkg/provisioner/ironic/factory.go +index 19571eb0..15f636b3 100644 +--- a/pkg/provisioner/ironic/factory.go ++++ b/pkg/provisioner/ironic/factory.go +@@ -114,10 +114,8 @@ func loadConfigFromEnv(havePreprovImgBuilder bool) (ironicConfig, error) { + c.deployRamdiskURL = os.Getenv("DEPLOY_RAMDISK_URL") + c.deployISOURL = os.Getenv("DEPLOY_ISO_URL") + if !havePreprovImgBuilder { +- if c.deployISOURL == "" && +- (c.deployKernelURL == "" || c.deployRamdiskURL == "") { +- return c, errors.New("either DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL or DEPLOY_ISO_URL must be set") +- } ++ // NOTE(dtantsur): with a PreprovisioningImage controller, it makes sense to set only the kernel. ++ // Without it, either both or neither must be set. + if (c.deployKernelURL == "" && c.deployRamdiskURL != "") || + (c.deployKernelURL != "" && c.deployRamdiskURL == "") { + return c, errors.New("DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL can only be set together") +diff --git a/pkg/provisioner/ironic/factory_test.go b/pkg/provisioner/ironic/factory_test.go +index db47d8b2..0d32eccb 100644 +--- a/pkg/provisioner/ironic/factory_test.go ++++ b/pkg/provisioner/ironic/factory_test.go +@@ -98,24 +98,19 @@ func TestLoadConfigFromEnv(t *testing.T) { + ramdiskURL: "http://ramdisk", + }, + }, +- { +- name: "no deploy info", +- env: EnvFixture{}, +- expectedError: "either DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL or DEPLOY_ISO_URL must be set", +- }, + { + name: "only kernel", + env: EnvFixture{ + kernelURL: "http://kernel", + }, +- expectedError: "either DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL or DEPLOY_ISO_URL must be set", ++ expectedError: "DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL can only be set together", + }, + { + name: "only ramdisk", + env: EnvFixture{ + ramdiskURL: "http://ramdisk", + }, +- expectedError: "either DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL or DEPLOY_ISO_URL must be set", ++ expectedError: "DEPLOY_KERNEL_URL and DEPLOY_RAMDISK_URL can only be set together", + expectedImgBuildError: "DEPLOY_RAMDISK_URL requires DEPLOY_KERNEL_URL to be set also", + }, + { +diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go +index 4c4923ad..48db865a 100644 +--- a/pkg/provisioner/ironic/ironic.go ++++ b/pkg/provisioner/ironic/ironic.go +@@ -348,14 +348,10 @@ func (p *ironicProvisioner) configureImages(data provisioner.ManagementAccessDat + switch data.State { + case metal3api.StateInspecting, + metal3api.StatePreparing: +- if deployImageInfo == nil { +- if p.config.havePreprovImgBuilder { +- result, err = transientError(provisioner.ErrNeedsPreprovisioningImage) +- } else { +- result, err = operationFailed("no preprovisioning image available") +- } +- return result, err ++ if deployImageInfo == nil && p.config.havePreprovImgBuilder { ++ result, err = transientError(provisioner.ErrNeedsPreprovisioningImage) + } ++ return result, err + default: + } + +-- +2.50.1 + diff --git a/baremetal-operator/0003-Remove-DEPLOY_KERNEL_URL-from-deployment-scripts-for.patch b/baremetal-operator/0003-Remove-DEPLOY_KERNEL_URL-from-deployment-scripts-for.patch new file mode 100644 index 0000000..545ad00 --- /dev/null +++ b/baremetal-operator/0003-Remove-DEPLOY_KERNEL_URL-from-deployment-scripts-for.patch @@ -0,0 +1,49 @@ +From ea10df866f0fc491cac15ba5005f3b820e1ccecb Mon Sep 17 00:00:00 2001 +From: Dmitry Tantsur +Date: Wed, 2 Jul 2025 17:55:48 +0200 +Subject: [PATCH 3/5] Remove DEPLOY_KERNEL_URL from deployment scripts for main + +Signed-off-by: Dmitry Tantsur +(cherry picked from commit ddcf3d915819b6344f79fbcec3e28250b217a597) +--- + config/default/ironic.env | 2 -- + config/overlays/e2e/ironic.env | 2 -- + config/render/capm3.yaml | 2 -- + 3 files changed, 6 deletions(-) + +diff --git a/config/default/ironic.env b/config/default/ironic.env +index e72cb3c3..3fe36d25 100644 +--- a/config/default/ironic.env ++++ b/config/default/ironic.env +@@ -1,7 +1,5 @@ + HTTP_PORT=6180 + PROVISIONING_INTERFACE=eth2 + DHCP_RANGE=172.22.0.10,172.22.0.100 +-DEPLOY_KERNEL_URL=http://172.22.0.2:6180/images/ironic-python-agent.kernel +-DEPLOY_RAMDISK_URL=http://172.22.0.2:6180/images/ironic-python-agent.initramfs + IRONIC_ENDPOINT=http://172.22.0.2:6385/v1/ + CACHEURL=http://172.22.0.1/images +diff --git a/config/overlays/e2e/ironic.env b/config/overlays/e2e/ironic.env +index 44147ae0..6f200720 100644 +--- a/config/overlays/e2e/ironic.env ++++ b/config/overlays/e2e/ironic.env +@@ -1,3 +1 @@ +-DEPLOY_KERNEL_URL=http://192.168.222.1:6180/images/ironic-python-agent.kernel +-DEPLOY_RAMDISK_URL=http://192.168.222.1:6180/images/ironic-python-agent.initramfs + IRONIC_ENDPOINT=https://192.168.222.1:6385/v1/ +diff --git a/config/render/capm3.yaml b/config/render/capm3.yaml +index 42283193..7568288f 100644 +--- a/config/render/capm3.yaml ++++ b/config/render/capm3.yaml +@@ -2510,8 +2510,6 @@ subjects: + apiVersion: v1 + data: + CACHEURL: http://172.22.0.1/images +- DEPLOY_KERNEL_URL: http://172.22.0.2:6180/images/ironic-python-agent.kernel +- DEPLOY_RAMDISK_URL: http://172.22.0.2:6180/images/ironic-python-agent.initramfs + DHCP_RANGE: 172.22.0.10,172.22.0.100 + HTTP_PORT: "6180" + IRONIC_ENDPOINT: http://172.22.0.2:6385/v1/ +-- +2.50.1 + diff --git a/baremetal-operator/0004-Refactor-setting-various-Ironic-properties.patch b/baremetal-operator/0004-Refactor-setting-various-Ironic-properties.patch new file mode 100644 index 0000000..abedf04 --- /dev/null +++ b/baremetal-operator/0004-Refactor-setting-various-Ironic-properties.patch @@ -0,0 +1,422 @@ +From b2e8a1a42c95a3338c9c83a4781ba4744da5ff6a Mon Sep 17 00:00:00 2001 +From: Dmitry Tantsur +Date: Tue, 24 Jun 2025 18:53:42 +0200 +Subject: [PATCH 4/5] Refactor setting various Ironic properties + +Currently, Ironic instance_info and properties fields are populated at +random either in most states or before deployment. While potentially +convenient, it makes it very hard to reason about the code. + +Now, the logic is split into two parts: +1. configureNode (renamed from configureImages) writes fields that are + considered properties of the node itself: CPU architecture, deploy + images, capabilities, etc. +2. getInstanceUpdateOpts (merge of getImageUpdateOptsForNode and + getUpdateOptsForNode) writes fields that are required for deployment + and thus are properties of instance. This includes images, checksums, + runtime capabilities. As an exception, root device hints fall under + this category and thus are now set in instance_info, not properties. + +Signed-off-by: Dmitry Tantsur +(cherry picked from commit 0c70cba38c926c474f4fa129a7e99ef9827d6ce9) +--- + .../metal3.io/baremetalhost_controller.go | 2 +- + pkg/provisioner/ironic/ironic.go | 49 +++++------- + pkg/provisioner/ironic/provision_test.go | 27 +++---- + pkg/provisioner/ironic/register.go | 3 +- + pkg/provisioner/ironic/register_test.go | 78 +------------------ + pkg/provisioner/provisioner.go | 2 +- + 6 files changed, 40 insertions(+), 121 deletions(-) + +diff --git a/internal/controller/metal3.io/baremetalhost_controller.go b/internal/controller/metal3.io/baremetalhost_controller.go +index 1998627e..0d0c9562 100644 +--- a/internal/controller/metal3.io/baremetalhost_controller.go ++++ b/internal/controller/metal3.io/baremetalhost_controller.go +@@ -848,6 +848,7 @@ func (r *BareMetalHostReconciler) registerHost(prov provisioner.Provisioner, inf + PreprovisioningNetworkData: preprovisioningNetworkData, + HasCustomDeploy: hasCustomDeploy(info.host), + DisablePowerOff: info.host.Spec.DisablePowerOff, ++ CPUArchitecture: getHostArchitecture(info.host), + }, + credsChanged, + info.host.Status.ErrorType == metal3api.RegistrationError) +@@ -1271,7 +1272,6 @@ func (r *BareMetalHostReconciler) actionProvisioning(prov provisioner.Provisione + BootMode: info.host.Status.Provisioning.BootMode, + HardwareProfile: hwProf, + RootDeviceHints: info.host.Status.Provisioning.RootDeviceHints.DeepCopy(), +- CPUArchitecture: getHostArchitecture(info.host), + }, forceReboot) + if err != nil { + return actionError{errors.Wrap(err, "failed to provision")} +diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go +index 48db865a..b8e6d72b 100644 +--- a/pkg/provisioner/ironic/ironic.go ++++ b/pkg/provisioner/ironic/ironic.go +@@ -311,20 +311,24 @@ func (p *ironicProvisioner) createPXEEnabledNodePort(uuid, macAddress string) er + return nil + } + +-func (p *ironicProvisioner) configureImages(data provisioner.ManagementAccessData, ironicNode *nodes.Node, bmcAccess bmc.AccessDetails) (result provisioner.Result, err error) { ++func (p *ironicProvisioner) configureNode(data provisioner.ManagementAccessData, ironicNode *nodes.Node, bmcAccess bmc.AccessDetails) (result provisioner.Result, err error) { + updater := clients.UpdateOptsBuilder(p.log) + + deployImageInfo := setDeployImage(p.config, bmcAccess, data.PreprovisioningImage) + updater.SetDriverInfoOpts(deployImageInfo, ironicNode) + +- // NOTE(dtantsur): It is risky to update image information for active nodes since it may affect the ability to clean up. +- if (data.CurrentImage != nil || data.HasCustomDeploy) && ironicNode.ProvisionState != string(nodes.Active) { +- p.getImageUpdateOptsForNode(ironicNode, data.CurrentImage, data.BootMode, data.HasCustomDeploy, updater) +- } + updater.SetTopLevelOpt("automated_clean", + data.AutomatedCleaningMode != metal3api.CleaningModeDisabled, + ironicNode.AutomatedClean) + ++ opts := clients.UpdateOptsData{ ++ "capabilities": buildCapabilitiesValue(ironicNode, data.BootMode), ++ } ++ if data.CPUArchitecture != "" { ++ opts["cpu_arch"] = data.CPUArchitecture ++ } ++ updater.SetPropertiesOpts(opts, ironicNode) ++ + _, success, result, err := p.tryUpdateNode(ironicNode, updater) + if !success { + return result, err +@@ -656,40 +660,29 @@ func (p *ironicProvisioner) setCustomDeployUpdateOptsForNode(ironicNode *nodes.N + SetTopLevelOpt("deploy_interface", "custom-agent", ironicNode.DeployInterface) + } + +-func (p *ironicProvisioner) getImageUpdateOptsForNode(ironicNode *nodes.Node, imageData *metal3api.Image, bootMode metal3api.BootMode, hasCustomDeploy bool, updater *clients.NodeUpdater) { ++func (p *ironicProvisioner) getInstanceUpdateOpts(ironicNode *nodes.Node, data provisioner.ProvisionData) *clients.NodeUpdater { ++ updater := clients.UpdateOptsBuilder(p.log) ++ ++ hasCustomDeploy := data.CustomDeploy != nil && data.CustomDeploy.Method != "" ++ + // instance_uuid + updater.SetTopLevelOpt("instance_uuid", string(p.objectMeta.UID), ironicNode.InstanceUUID) + + updater.SetInstanceInfoOpts(clients.UpdateOptsData{ +- "capabilities": buildInstanceInfoCapabilities(bootMode), ++ "capabilities": buildInstanceInfoCapabilities(data.BootMode), ++ "root_device": devicehints.MakeHintMap(data.RootDeviceHints), + }, ironicNode) + + if hasCustomDeploy { + // Custom deploy process +- p.setCustomDeployUpdateOptsForNode(ironicNode, imageData, updater) +- } else if imageData.IsLiveISO() { ++ p.setCustomDeployUpdateOptsForNode(ironicNode, &data.Image, updater) ++ } else if data.Image.IsLiveISO() { + // Set live-iso format options +- p.setLiveIsoUpdateOptsForNode(ironicNode, imageData, updater) ++ p.setLiveIsoUpdateOptsForNode(ironicNode, &data.Image, updater) + } else { + // Set deploy_interface direct options when not booting a live-iso +- p.setDirectDeployUpdateOptsForNode(ironicNode, imageData, updater) ++ p.setDirectDeployUpdateOptsForNode(ironicNode, &data.Image, updater) + } +-} +- +-func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node, data provisioner.ProvisionData) *clients.NodeUpdater { +- updater := clients.UpdateOptsBuilder(p.log) +- +- hasCustomDeploy := data.CustomDeploy != nil && data.CustomDeploy.Method != "" +- p.getImageUpdateOptsForNode(ironicNode, &data.Image, data.BootMode, hasCustomDeploy, updater) +- +- opts := clients.UpdateOptsData{ +- "root_device": devicehints.MakeHintMap(data.RootDeviceHints), +- "capabilities": buildCapabilitiesValue(ironicNode, data.BootMode), +- } +- if data.CPUArchitecture != "" { +- opts["cpu_arch"] = data.CPUArchitecture +- } +- updater.SetPropertiesOpts(opts, ironicNode) + + return updater + } +@@ -792,7 +785,7 @@ func (p *ironicProvisioner) setUpForProvisioning(ironicNode *nodes.Node, data pr + p.log.Info("starting provisioning", "node properties", ironicNode.Properties) + + ironicNode, success, result, err := p.tryUpdateNode(ironicNode, +- p.getUpdateOptsForNode(ironicNode, data)) ++ p.getInstanceUpdateOpts(ironicNode, data)) + if !success { + return result, err + } +diff --git a/pkg/provisioner/ironic/provision_test.go b/pkg/provisioner/ironic/provision_test.go +index 72ee57b7..40c714e9 100644 +--- a/pkg/provisioner/ironic/provision_test.go ++++ b/pkg/provisioner/ironic/provision_test.go +@@ -713,7 +713,7 @@ func TestGetUpdateOptsForNodeWithRootHints(t *testing.T) { + BootMode: metal3api.DefaultBootMode, + RootDeviceHints: host.Status.Provisioning.RootDeviceHints, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -723,7 +723,7 @@ func TestGetUpdateOptsForNodeWithRootHints(t *testing.T) { + Value interface{} // the value being passed to ironic (or value associated with the key) + }{ + { +- Path: "/properties/root_device", ++ Path: "/instance_info/root_device", + Value: "userdefined_devicename", + Map: map[string]string{ + "name": "s== userd_devicename", +@@ -807,7 +807,7 @@ func TestGetUpdateOptsForNodeVirtual(t *testing.T) { + BootMode: metal3api.DefaultBootMode, + HardwareProfile: hwProf, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -903,9 +903,8 @@ func TestGetUpdateOptsForNodeDell(t *testing.T) { + Image: *host.Spec.Image, + BootMode: metal3api.DefaultBootMode, + HardwareProfile: hwProf, +- CPUArchitecture: "x86_64", + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -930,10 +929,6 @@ func TestGetUpdateOptsForNodeDell(t *testing.T) { + Path: "/instance_uuid", + Value: "27720611-e5d1-45d3-ba3a-222dcfaa4ca2", + }, +- { +- Path: "/properties/cpu_arch", +- Value: "x86_64", +- }, + } + + for _, e := range expected { +@@ -971,7 +966,7 @@ func TestGetUpdateOptsForNodeLiveIso(t *testing.T) { + Image: *host.Spec.Image, + BootMode: metal3api.DefaultBootMode, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -1038,7 +1033,7 @@ func TestGetUpdateOptsForNodeImageToLiveIso(t *testing.T) { + Image: *host.Spec.Image, + BootMode: metal3api.DefaultBootMode, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -1116,7 +1111,7 @@ func TestGetUpdateOptsForNodeLiveIsoToImage(t *testing.T) { + Image: *host.Spec.Image, + BootMode: metal3api.DefaultBootMode, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -1188,7 +1183,7 @@ func TestGetUpdateOptsForNodeCustomDeploy(t *testing.T) { + BootMode: metal3api.DefaultBootMode, + CustomDeploy: host.Spec.CustomDeploy, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -1245,7 +1240,7 @@ func TestGetUpdateOptsForNodeCustomDeployWithImage(t *testing.T) { + BootMode: metal3api.DefaultBootMode, + CustomDeploy: host.Spec.CustomDeploy, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -1312,7 +1307,7 @@ func TestGetUpdateOptsForNodeImageToCustomDeploy(t *testing.T) { + BootMode: metal3api.DefaultBootMode, + CustomDeploy: host.Spec.CustomDeploy, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +@@ -1405,7 +1400,7 @@ func TestGetUpdateOptsForNodeSecureBoot(t *testing.T) { + BootMode: metal3api.UEFISecureBoot, + HardwareProfile: hwProf, + } +- patches := prov.getUpdateOptsForNode(ironicNode, provData).Updates ++ patches := prov.getInstanceUpdateOpts(ironicNode, provData).Updates + + t.Logf("patches: %v", patches) + +diff --git a/pkg/provisioner/ironic/register.go b/pkg/provisioner/ironic/register.go +index 390e463f..9a600189 100644 +--- a/pkg/provisioner/ironic/register.go ++++ b/pkg/provisioner/ironic/register.go +@@ -220,7 +220,7 @@ func (p *ironicProvisioner) Register(data provisioner.ManagementAccessData, cred + fallthrough + + default: +- result, err = p.configureImages(data, ironicNode, bmcAccess) ++ result, err = p.configureNode(data, ironicNode, bmcAccess) + return result, provID, err + } + } +@@ -246,6 +246,7 @@ func (p *ironicProvisioner) enrollNode(data provisioner.ManagementAccessData, bm + DisablePowerOff: &data.DisablePowerOff, + Properties: map[string]interface{}{ + "capabilities": buildCapabilitiesValue(nil, data.BootMode), ++ "cpu_arch": data.CPUArchitecture, + }, + } + +diff --git a/pkg/provisioner/ironic/register_test.go b/pkg/provisioner/ironic/register_test.go +index e6c302b5..8e524dad 100644 +--- a/pkg/provisioner/ironic/register_test.go ++++ b/pkg/provisioner/ironic/register_test.go +@@ -72,7 +72,7 @@ func TestRegisterMACOptional(t *testing.T) { + assert.Equal(t, "", result.ErrorMessage) + } + +-func TestRegisterCreateNodeNoImage(t *testing.T) { ++func TestRegisterCreateNode(t *testing.T) { + // Create a host without a bootMACAddress and with a BMC that + // does not require one. + host := makeHost() +@@ -146,79 +146,6 @@ func TestRegisterCreateNodeOldInspection(t *testing.T) { + assert.Equal(t, "inspector", createdNode.InspectInterface) + } + +-func TestRegisterCreateWithImage(t *testing.T) { +- // Create a host with Image specified in the Spec +- host := makeHost() +- host.Status.Provisioning.ID = "" // so we don't lookup by uuid +- host.Spec.Image.URL = "theimagefoo" +- host.Spec.Image.Checksum = "thechecksumxyz" +- host.Spec.Image.ChecksumType = "auto" +- +- var createdNode *nodes.Node +- +- createCallback := func(node nodes.Node) { +- createdNode = &node +- } +- +- ironic := testserver.NewIronic(t).WithDrivers().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) +- ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}") +- ironic.Start() +- defer ironic.Stop() +- +- auth := clients.AuthConfig{Type: clients.NoAuth} +- prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nullEventPublisher, ironic.Endpoint(), auth) +- if err != nil { +- t.Fatalf("could not create provisioner: %s", err) +- } +- +- result, provID, err := prov.Register(provisioner.ManagementAccessData{CurrentImage: host.Spec.Image.DeepCopy()}, false, false) +- if err != nil { +- t.Fatalf("error from Register: %s", err) +- } +- assert.Equal(t, "", result.ErrorMessage) +- assert.Equal(t, createdNode.UUID, provID) +- assert.Equal(t, "", createdNode.DeployInterface) +- updates, _ := ironic.GetLastRequestFor("/v1/nodes/node-0", http.MethodPatch) +- assert.Contains(t, updates, "/instance_info/image_source") +- assert.Contains(t, updates, host.Spec.Image.URL) +- assert.Contains(t, updates, "/instance_info/image_checksum") +- assert.Contains(t, updates, host.Spec.Image.Checksum) +-} +- +-func TestRegisterCreateWithLiveIso(t *testing.T) { +- // Create a host with Image specified in the Spec +- host := makeHostLiveIso() +- host.Status.Provisioning.ID = "" // so we don't lookup by uuid +- +- var createdNode *nodes.Node +- +- createCallback := func(node nodes.Node) { +- createdNode = &node +- } +- +- ironic := testserver.NewIronic(t).WithDrivers().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) +- ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}") +- ironic.Start() +- defer ironic.Stop() +- +- auth := clients.AuthConfig{Type: clients.NoAuth} +- prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nullEventPublisher, ironic.Endpoint(), auth) +- if err != nil { +- t.Fatalf("could not create provisioner: %s", err) +- } +- +- result, provID, err := prov.Register(provisioner.ManagementAccessData{CurrentImage: host.Spec.Image.DeepCopy()}, false, false) +- if err != nil { +- t.Fatalf("error from Register: %s", err) +- } +- assert.Equal(t, "", result.ErrorMessage) +- assert.Equal(t, createdNode.UUID, provID) +- assert.Equal(t, "ramdisk", createdNode.DeployInterface) +- updates, _ := ironic.GetLastRequestFor("/v1/nodes/node-0", http.MethodPatch) +- assert.Contains(t, updates, "/instance_info/boot_iso") +- assert.Contains(t, updates, host.Spec.Image.URL) +-} +- + func TestRegisterExistingNode(t *testing.T) { + // Create a host without a bootMACAddress and with a BMC that + // does not require one. +@@ -342,6 +269,7 @@ func TestRegisterExistingNodeContinue(t *testing.T) { + "test_password": "******", // ironic returns a placeholder + "test_port": "42", + }, ++ Properties: map[string]interface{}{"capabilities": ""}, + }).NodeUpdate(nodes.Node{ + UUID: "uuid", + }) +@@ -521,6 +449,7 @@ func TestRegisterExistingSteadyStateNoUpdate(t *testing.T) { + DeployInterface: imageType.DeployInterface, + InstanceInfo: imageType.InstanceInfo, + DriverInfo: imageType.DriverInfo, ++ Properties: map[string]interface{}{"capabilities": ""}, + }).NodeUpdate(nodes.Node{ + UUID: "uuid", + }) +@@ -577,6 +506,7 @@ func TestRegisterExistingNodeWaiting(t *testing.T) { + "test_password": "******", // ironic returns a placeholder + "test_port": "42", + }, ++ Properties: map[string]interface{}{"capabilities": ""}, + } + ironic := testserver.NewIronic(t).CreateNodes(createCallback).Node(node).NodeUpdate(nodes.Node{ + UUID: "uuid", +diff --git a/pkg/provisioner/provisioner.go b/pkg/provisioner/provisioner.go +index faddd0fd..e2018e63 100644 +--- a/pkg/provisioner/provisioner.go ++++ b/pkg/provisioner/provisioner.go +@@ -82,6 +82,7 @@ type ManagementAccessData struct { + PreprovisioningNetworkData string + HasCustomDeploy bool + DisablePowerOff bool ++ CPUArchitecture string + } + + type AdoptData struct { +@@ -122,7 +123,6 @@ type ProvisionData struct { + HardwareProfile profile.Profile + RootDeviceHints *metal3api.RootDeviceHints + CustomDeploy *metal3api.CustomDeploy +- CPUArchitecture string + } + + type HTTPHeaders []map[string]string +-- +2.50.1 + diff --git a/baremetal-operator/0005-Provide-inline-docs-for-node-configuration-calls.patch b/baremetal-operator/0005-Provide-inline-docs-for-node-configuration-calls.patch new file mode 100644 index 0000000..7a2ee61 --- /dev/null +++ b/baremetal-operator/0005-Provide-inline-docs-for-node-configuration-calls.patch @@ -0,0 +1,46 @@ +From 5419f8d95306efed8667936156d8081c21e068ed Mon Sep 17 00:00:00 2001 +From: Dmitry Tantsur +Date: Wed, 9 Jul 2025 14:02:23 +0200 +Subject: [PATCH 5/5] Provide inline docs for node configuration calls + +Signed-off-by: Dmitry Tantsur +(cherry picked from commit 778d9342747aefc8079f1ccaa6a14f83b26f28ff) +--- + pkg/provisioner/ironic/ironic.go | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go +index b8e6d72b..166d929c 100644 +--- a/pkg/provisioner/ironic/ironic.go ++++ b/pkg/provisioner/ironic/ironic.go +@@ -311,6 +311,10 @@ func (p *ironicProvisioner) createPXEEnabledNodePort(uuid, macAddress string) er + return nil + } + ++// configureNode configures Node properties that are not related to any specific provisioning phase. ++// It populates the AutomatedClean field, as well as capabilities and architecture in Properties. ++// It also calls setDeployImage to populate IPA parameters in DriverInfo and ++// checks if the required PreprovisioningImage is provided and ready. + func (p *ironicProvisioner) configureNode(data provisioner.ManagementAccessData, ironicNode *nodes.Node, bmcAccess bmc.AccessDetails) (result provisioner.Result, err error) { + updater := clients.UpdateOptsBuilder(p.log) + +@@ -426,6 +430,8 @@ func setExternalURL(p *ironicProvisioner, driverInfo map[string]interface{}) map + return driverInfo + } + ++// setDeployImage configures the IPA ramdisk parameters in the Node's DriverInfo. ++// It can use either the provided PreprovisioningImage or the global configuration from ironicConfig. + func setDeployImage(config ironicConfig, accessDetails bmc.AccessDetails, hostImage *provisioner.PreprovisioningImage) clients.UpdateOptsData { + deployImageInfo := clients.UpdateOptsData{ + deployKernelKey: nil, +@@ -660,6 +666,7 @@ func (p *ironicProvisioner) setCustomDeployUpdateOptsForNode(ironicNode *nodes.N + SetTopLevelOpt("deploy_interface", "custom-agent", ironicNode.DeployInterface) + } + ++// getInstanceUpdateOpts constructs InstanceInfo options required to provision a Node in Ironic. + func (p *ironicProvisioner) getInstanceUpdateOpts(ironicNode *nodes.Node, data provisioner.ProvisionData) *clients.NodeUpdater { + updater := clients.UpdateOptsBuilder(p.log) + +-- +2.50.1 + diff --git a/baremetal-operator/baremetal-operator.spec b/baremetal-operator/baremetal-operator.spec index b62bcbb..8cf0824 100644 --- a/baremetal-operator/baremetal-operator.spec +++ b/baremetal-operator/baremetal-operator.spec @@ -24,6 +24,13 @@ License: Apache-2.0 URL: https://github.com/metal3-io/baremetal-operator Source: baremetal-operator-%{version}.tar Source1: vendor.tar.gz +# Patches related to multi-architecture support, upstream PRs #2506 #2559 #2537 +Patch0: 0001-Enable-exhaustive-linter.patch +Patch1: 0002-Stop-requiring-DEPLOY_KERNEL-RAMDISK.patch +Patch2: 0003-Remove-DEPLOY_KERNEL_URL-from-deployment-scripts-for.patch +Patch3: 0004-Refactor-setting-various-Ironic-properties.patch +Patch4: 0005-Provide-inline-docs-for-node-configuration-calls.patch + BuildRequires: golang(API) = 1.24 ExcludeArch: s390 ExcludeArch: %{ix86} -- 2.49.0 From 7be5f59e4f9c5ce3aad1d9607d74b4c977f28da522b09a7859a940c1c185ad33 Mon Sep 17 00:00:00 2001 From: Nicolas Belouin Date: Wed, 3 Sep 2025 16:14:41 +0200 Subject: [PATCH 3/3] Bump metal3 versions Signed-off-by: Nicolas Belouin --- baremetal-operator-image/Dockerfile | 6 +++--- ironic-image/Dockerfile | 8 ++++---- ironic-ipa-downloader-image/Dockerfile | 8 ++++---- metal3-chart/Chart.yaml | 14 +++++++------- metal3-chart/charts/baremetal-operator/Chart.yaml | 2 +- metal3-chart/charts/baremetal-operator/values.yaml | 2 +- metal3-chart/charts/ironic/Chart.yaml | 2 +- metal3-chart/charts/ironic/values.yaml | 4 ++-- metal3-chart/charts/media/Chart.yaml | 2 +- metal3-chart/charts/media/values.yaml | 2 +- release-manifest-image/release_manifest.yaml | 2 +- 11 files changed, 26 insertions(+), 26 deletions(-) diff --git a/baremetal-operator-image/Dockerfile b/baremetal-operator-image/Dockerfile index 0b23023..3e34e57 100644 --- a/baremetal-operator-image/Dockerfile +++ b/baremetal-operator-image/Dockerfile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 -#!BuildTag: %%IMG_PREFIX%%baremetal-operator:%%baremetal-operator_version%%.0 -#!BuildTag: %%IMG_PREFIX%%baremetal-operator:%%baremetal-operator_version%%.0-%RELEASE% +#!BuildTag: %%IMG_PREFIX%%baremetal-operator:%%baremetal-operator_version%%.1 +#!BuildTag: %%IMG_PREFIX%%baremetal-operator:%%baremetal-operator_version%%.1-%RELEASE% ARG SLE_VERSION FROM registry.suse.com/bci/bci-micro:$SLE_VERSION AS micro @@ -18,7 +18,7 @@ LABEL org.opencontainers.image.version="%%baremetal-operator_version%%" LABEL org.opencontainers.image.url="https://www.suse.com/products/server/" LABEL org.opencontainers.image.created="%BUILDTIME%" LABEL org.opencontainers.image.vendor="SUSE LLC" -LABEL org.opensuse.reference="%%IMG_REPO%%/%%IMG_PREFIX%%baremetal-operator:%%baremetal-operator_version%%.0-%RELEASE%" +LABEL org.opensuse.reference="%%IMG_REPO%%/%%IMG_PREFIX%%baremetal-operator:%%baremetal-operator_version%%.1-%RELEASE%" LABEL org.openbuildservice.disturl="%DISTURL%" LABEL com.suse.supportlevel="%%SUPPORT_LEVEL%%" LABEL com.suse.eula="SUSE Combined EULA February 2024" diff --git a/ironic-image/Dockerfile b/ironic-image/Dockerfile index a5fa085..b2b309d 100644 --- a/ironic-image/Dockerfile +++ b/ironic-image/Dockerfile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 -#!BuildTag: %%IMG_PREFIX%%ironic:29.0.4.1 -#!BuildTag: %%IMG_PREFIX%%ironic:29.0.4.1-%RELEASE% +#!BuildTag: %%IMG_PREFIX%%ironic:29.0.4.2 +#!BuildTag: %%IMG_PREFIX%%ironic:29.0.4.2-%RELEASE% ARG SLE_VERSION FROM registry.suse.com/bci/bci-micro:$SLE_VERSION AS micro @@ -41,8 +41,8 @@ LABEL org.opencontainers.image.description="Openstack Ironic based on the SLE Ba LABEL org.opencontainers.image.url="https://www.suse.com/products/server/" LABEL org.opencontainers.image.created="%BUILDTIME%" LABEL org.opencontainers.image.vendor="SUSE LLC" -LABEL org.opencontainers.image.version="29.0.4.1" -LABEL org.opensuse.reference="%%IMG_REPO%%/%%IMG_PREFIX%%ironic:29.0.4.1-%RELEASE%" +LABEL org.opencontainers.image.version="29.0.4.2" +LABEL org.opensuse.reference="%%IMG_REPO%%/%%IMG_PREFIX%%ironic:29.0.4.2-%RELEASE%" LABEL org.openbuildservice.disturl="%DISTURL%" LABEL com.suse.supportlevel="%%SUPPORT_LEVEL%%" LABEL com.suse.eula="SUSE Combined EULA February 2024" diff --git a/ironic-ipa-downloader-image/Dockerfile b/ironic-ipa-downloader-image/Dockerfile index b39fec5..485db0e 100644 --- a/ironic-ipa-downloader-image/Dockerfile +++ b/ironic-ipa-downloader-image/Dockerfile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 -#!BuildTag: %%IMG_PREFIX%%ironic-ipa-downloader:3.0.8 -#!BuildTag: %%IMG_PREFIX%%ironic-ipa-downloader:3.0.8-%RELEASE% +#!BuildTag: %%IMG_PREFIX%%ironic-ipa-downloader:3.0.9 +#!BuildTag: %%IMG_PREFIX%%ironic-ipa-downloader:3.0.9-%RELEASE% ARG SLE_VERSION FROM registry.suse.com/bci/bci-micro:$SLE_VERSION AS micro @@ -18,11 +18,11 @@ FROM micro AS final LABEL org.opencontainers.image.authors="SUSE LLC (https://www.suse.com/)" LABEL org.opencontainers.image.title="SLE Based Ironic IPA Downloader Container Image" LABEL org.opencontainers.image.description="ironic-ipa-downloader based on the SLE Base Container Image." -LABEL org.opencontainers.image.version="3.0.8" +LABEL org.opencontainers.image.version="3.0.9" LABEL org.opencontainers.image.url="https://www.suse.com/solutions/edge-computing/" LABEL org.opencontainers.image.created="%BUILDTIME%" LABEL org.opencontainers.image.vendor="SUSE LLC" -LABEL org.opensuse.reference="%%IMG_REPO%%/%%IMG_PREFIX%%ironic-ipa-downloader:3.0.8-%RELEASE%" +LABEL org.opensuse.reference="%%IMG_REPO%%/%%IMG_PREFIX%%ironic-ipa-downloader:3.0.9-%RELEASE%" LABEL org.openbuildservice.disturl="%DISTURL%" LABEL com.suse.supportlevel="%%SUPPORT_LEVEL%%" LABEL com.suse.eula="SUSE Combined EULA February 2024" diff --git a/metal3-chart/Chart.yaml b/metal3-chart/Chart.yaml index 2fce13d..8207b33 100644 --- a/metal3-chart/Chart.yaml +++ b/metal3-chart/Chart.yaml @@ -1,16 +1,16 @@ -#!BuildTag: %%CHART_PREFIX%%metal3:%%CHART_MAJOR%%.0.14_up0.12.4 -#!BuildTag: %%CHART_PREFIX%%metal3:%%CHART_MAJOR%%.0.14_up0.12.4-%RELEASE% +#!BuildTag: %%CHART_PREFIX%%metal3:%%CHART_MAJOR%%.0.15_up0.12.5 +#!BuildTag: %%CHART_PREFIX%%metal3:%%CHART_MAJOR%%.0.15_up0.12.5-%RELEASE% apiVersion: v2 -appVersion: 0.12.4 +appVersion: 0.12.5 dependencies: - alias: metal3-baremetal-operator name: baremetal-operator repository: file://./charts/baremetal-operator - version: 0.10.3 + version: 0.10.4 - alias: metal3-ironic name: ironic repository: file://./charts/ironic - version: 0.11.2 + version: 0.11.3 - alias: metal3-mariadb condition: global.enable_mariadb name: mariadb @@ -20,9 +20,9 @@ dependencies: condition: global.enable_metal3_media_server name: media repository: file://./charts/media - version: 0.6.5 + version: 0.6.6 description: A Helm chart that installs all of the dependencies needed for Metal3 icon: https://github.com/cncf/artwork/raw/master/projects/metal3/icon/color/metal3-icon-color.svg name: metal3 type: application -version: "%%CHART_MAJOR%%.0.14+up0.12.4" +version: "%%CHART_MAJOR%%.0.15+up0.12.5" diff --git a/metal3-chart/charts/baremetal-operator/Chart.yaml b/metal3-chart/charts/baremetal-operator/Chart.yaml index 6a3ee5f..edf9219 100644 --- a/metal3-chart/charts/baremetal-operator/Chart.yaml +++ b/metal3-chart/charts/baremetal-operator/Chart.yaml @@ -3,4 +3,4 @@ appVersion: 0.10.2 description: A Helm chart for baremetal-operator, used by Metal3 name: baremetal-operator type: application -version: 0.10.3 +version: 0.10.4 diff --git a/metal3-chart/charts/baremetal-operator/values.yaml b/metal3-chart/charts/baremetal-operator/values.yaml index b7530d1..870fb73 100644 --- a/metal3-chart/charts/baremetal-operator/values.yaml +++ b/metal3-chart/charts/baremetal-operator/values.yaml @@ -28,7 +28,7 @@ images: baremetalOperator: repository: registry.opensuse.org/isv/suse/edge/metal3/containers/images/baremetal-operator pullPolicy: IfNotPresent - tag: "0.10.2.0" + tag: "0.10.2.1" imagePullSecrets: [] nameOverride: "manger" diff --git a/metal3-chart/charts/ironic/Chart.yaml b/metal3-chart/charts/ironic/Chart.yaml index 4aaafad..f5b7560 100644 --- a/metal3-chart/charts/ironic/Chart.yaml +++ b/metal3-chart/charts/ironic/Chart.yaml @@ -3,4 +3,4 @@ appVersion: 29.0.4 description: A Helm chart for Ironic, used by Metal3 name: ironic type: application -version: 0.11.2 +version: 0.11.3 diff --git a/metal3-chart/charts/ironic/values.yaml b/metal3-chart/charts/ironic/values.yaml index 3ca47ae..8408176 100644 --- a/metal3-chart/charts/ironic/values.yaml +++ b/metal3-chart/charts/ironic/values.yaml @@ -64,11 +64,11 @@ images: ironic: repository: registry.opensuse.org/isv/suse/edge/metal3/containers/images/ironic pullPolicy: IfNotPresent - tag: 29.0.4.1 + tag: 29.0.4.2 ironicIPADownloader: repository: registry.opensuse.org/isv/suse/edge/metal3/containers/images/ironic-ipa-downloader pullPolicy: IfNotPresent - tag: 3.0.8 + tag: 3.0.9 nameOverride: "" fullnameOverride: "" diff --git a/metal3-chart/charts/media/Chart.yaml b/metal3-chart/charts/media/Chart.yaml index 2996fcd..283fa6f 100644 --- a/metal3-chart/charts/media/Chart.yaml +++ b/metal3-chart/charts/media/Chart.yaml @@ -3,4 +3,4 @@ appVersion: 1.16.0 description: A Helm chart for Media, used by Metal3 name: media type: application -version: 0.6.5 +version: 0.6.6 diff --git a/metal3-chart/charts/media/values.yaml b/metal3-chart/charts/media/values.yaml index 115511a..efa8c21 100644 --- a/metal3-chart/charts/media/values.yaml +++ b/metal3-chart/charts/media/values.yaml @@ -24,7 +24,7 @@ replicaCount: 1 image: repository: registry.opensuse.org/isv/suse/edge/metal3/containers/images/ironic pullPolicy: IfNotPresent - tag: 29.0.4.1 + tag: 29.0.4.2 imagePullSecrets: [] nameOverride: "" diff --git a/release-manifest-image/release_manifest.yaml b/release-manifest-image/release_manifest.yaml index 6a207bd..8a4ff5d 100644 --- a/release-manifest-image/release_manifest.yaml +++ b/release-manifest-image/release_manifest.yaml @@ -171,7 +171,7 @@ spec: - prettyName: Metal3 releaseName: metal3 chart: '%%CHART_REPO%%/%%CHART_PREFIX%%metal3' - version: '%%CHART_MAJOR%%.0.14+up0.12.4' + version: '%%CHART_MAJOR%%.0.15+up0.12.5' - prettyName: RancherTurtles releaseName: rancher-turtles chart: '%%CHART_REPO%%/%%CHART_PREFIX%%rancher-turtles' -- 2.49.0