From 6c6544a608c90512a50035f359da4cc29d1ad6b71919eb3d0326fdc0b8120bb9 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Mon, 7 Jul 2025 22:05:45 +0000 Subject: [PATCH 01/15] Align to v29.0.0 Signed-off-by: Marco Chiappero --- .../ironic-config/apache2-vmedia.conf.j2 | 14 ++-- ironic-image/ironic-config/dnsmasq.conf.j2 | 11 +-- .../ironic-config/httpd-ironic-api.conf.j2 | 6 +- ironic-image/ironic-config/httpd-modules.conf | 38 +++++----- ironic-image/ironic-config/httpd.conf.j2 | 8 +-- ironic-image/ironic-config/inspector.ipxe.j2 | 4 +- ironic-image/ironic-config/ironic.conf.j2 | 37 ++++++---- .../network-data-schema-empty.json | 1 - ironic-image/scripts/auth-common.sh | 70 ++++++++++++++----- ironic-image/scripts/configure-ironic.sh | 52 ++++---------- ironic-image/scripts/ironic-common.sh | 64 +++++++++++++---- ironic-image/scripts/ironic-probe.j2 | 9 --- ironic-image/scripts/ironic-probe.sh | 23 ++++++ ironic-image/scripts/rundatabase-upgrade | 10 +++ ironic-image/scripts/rundnsmasq | 16 +++-- ironic-image/scripts/runhttpd | 39 +++++------ ironic-image/scripts/runironic | 17 ++--- ironic-image/scripts/runironic-exporter | 14 ++++ ironic-image/scripts/runlogwatch.sh | 22 +++--- .../scripts/runonline-data-migrations | 10 +++ ironic-image/scripts/tls-common.sh | 18 +++++ 21 files changed, 298 insertions(+), 185 deletions(-) delete mode 100644 ironic-image/ironic-config/network-data-schema-empty.json mode change 100644 => 100755 ironic-image/scripts/configure-ironic.sh delete mode 100644 ironic-image/scripts/ironic-probe.j2 create mode 100755 ironic-image/scripts/ironic-probe.sh create mode 100755 ironic-image/scripts/rundatabase-upgrade mode change 100644 => 100755 ironic-image/scripts/rundnsmasq mode change 100644 => 100755 ironic-image/scripts/runhttpd mode change 100644 => 100755 ironic-image/scripts/runironic create mode 100755 ironic-image/scripts/runironic-exporter mode change 100644 => 100755 ironic-image/scripts/runlogwatch.sh create mode 100755 ironic-image/scripts/runonline-data-migrations diff --git a/ironic-image/ironic-config/apache2-vmedia.conf.j2 b/ironic-image/ironic-config/apache2-vmedia.conf.j2 index fd87d39..20dc4a7 100644 --- a/ironic-image/ironic-config/apache2-vmedia.conf.j2 +++ b/ironic-image/ironic-config/apache2-vmedia.conf.j2 @@ -10,15 +10,13 @@ Listen {{ env.VMEDIA_TLS_PORT }} SSLCertificateFile {{ env.IRONIC_VMEDIA_CERT_FILE }} SSLCertificateKeyFile {{ env.IRONIC_VMEDIA_KEY_FILE }} - - AllowOverride None - Require all granted + + Order deny,allow + deny from all - - - Options Indexes FollowSymLinks - AllowOverride None - Require all granted + + Order allow,deny + allow from all diff --git a/ironic-image/ironic-config/dnsmasq.conf.j2 b/ironic-image/ironic-config/dnsmasq.conf.j2 index ad58078..f216fed 100644 --- a/ironic-image/ironic-config/dnsmasq.conf.j2 +++ b/ironic-image/ironic-config/dnsmasq.conf.j2 @@ -3,6 +3,7 @@ bind-dynamic enable-tftp tftp-root=/shared/tftpboot log-queries +dhcp-leasefile=/data/dnsmasq/dnsmasq.leases # Configure listening for DNS (0 disables DNS) port={{ env.DNS_PORT }} @@ -31,11 +32,11 @@ dhcp-match=ipxe,175 # Client is already running iPXE; move to next stage of chainloading {%- if env.IPXE_TLS_SETUP == "true" %} # iPXE with (U)EFI -dhcp-boot=tag:efi,tag:ipxe,http://{{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}/custom-ipxe/snponly.efi +dhcp-boot=tag:efi,tag:ipxe,{{ env.IRONIC_HTTP_URL }}/custom-ipxe/snponly.efi # iPXE with BIOS -dhcp-boot=tag:ipxe,http://{{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}/custom-ipxe/undionly.kpxe +dhcp-boot=tag:ipxe,{{ env.IRONIC_HTTP_URL }}/custom-ipxe/undionly.kpxe {% else %} -dhcp-boot=tag:ipxe,http://{{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}/boot.ipxe +dhcp-boot=tag:ipxe,{{ env.IRONIC_HTTP_URL }}/boot.ipxe {% endif %} # Note: Need to test EFI booting @@ -59,8 +60,8 @@ ra-param={{ env.PROVISIONING_INTERFACE }},0,0 dhcp-vendorclass=set:pxe6,enterprise:343,PXEClient dhcp-userclass=set:ipxe6,iPXE -dhcp-option=tag:pxe6,option6:bootfile-url,tftp://{{ env.IRONIC_URL_HOST }}/snponly.efi -dhcp-option=tag:ipxe6,option6:bootfile-url,http://{{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}/boot.ipxe +dhcp-option=tag:pxe6,option6:bootfile-url,{{ env.IRONIC_TFTP_URL }}/snponly.efi +dhcp-option=tag:ipxe6,option6:bootfile-url,{{ env.IRONIC_HTTP_URL }}/boot.ipxe # It can be used when setting DNS or GW variables. {%- if env["GATEWAY_IP"] is undefined %} diff --git a/ironic-image/ironic-config/httpd-ironic-api.conf.j2 b/ironic-image/ironic-config/httpd-ironic-api.conf.j2 index 15c73b6..a374894 100644 --- a/ironic-image/ironic-config/httpd-ironic-api.conf.j2 +++ b/ironic-image/ironic-config/httpd-ironic-api.conf.j2 @@ -27,8 +27,8 @@ Listen {{ env.IRONIC_URL_HOST }}:{{ env.IRONIC_LISTEN_PORT }} ProxyPassReverse "/" "http://127.0.0.1:{{ env.IRONIC_PRIVATE_PORT }}/" {% endif %} - SetEnv APACHE_RUN_USER ironic-suse - SetEnv APACHE_RUN_GROUP ironic-suse + SetEnv APACHE_RUN_USER ironic + SetEnv APACHE_RUN_GROUP ironic ErrorLog /dev/stderr LogLevel debug @@ -45,7 +45,7 @@ Listen {{ env.IRONIC_URL_HOST }}:{{ env.IRONIC_LISTEN_PORT }} {% if "IRONIC_HTPASSWD" in env and env.IRONIC_HTPASSWD | length %} AuthType Basic AuthName "Restricted area" - AuthUserFile "/etc/ironic/htpasswd" + AuthUserFile {{ env.HTPASSWD_FILE }} Require valid-user {% endif %} diff --git a/ironic-image/ironic-config/httpd-modules.conf b/ironic-image/ironic-config/httpd-modules.conf index fd2bc99..5984994 100644 --- a/ironic-image/ironic-config/httpd-modules.conf +++ b/ironic-image/ironic-config/httpd-modules.conf @@ -1,20 +1,20 @@ # Bare minimum set of modules -LoadModule log_config_module /usr/lib64/apache2/mod_log_config.so -LoadModule mime_module /usr/lib64/apache2/mod_mime.so -LoadModule dir_module /usr/lib64/apache2/mod_dir.so -LoadModule authz_core_module /usr/lib64/apache2/mod_authz_core.so -#LoadModule unixd_module modules/mod_unixd.so -#LoadModule mpm_event_module modules/mod_mpm_event.so -LoadModule ssl_module /usr/lib64/apache2/mod_ssl.so -LoadModule env_module /usr/lib64/apache2/mod_env.so -LoadModule proxy_module /usr/lib64/apache2/mod_proxy.so -LoadModule proxy_ajp_module /usr/lib64/apache2/mod_proxy_ajp.so -LoadModule proxy_balancer_module /usr/lib64/apache2/mod_proxy_balancer.so -LoadModule proxy_http_module /usr/lib64/apache2/mod_proxy_http.so -LoadModule slotmem_shm_module /usr/lib64/apache2/mod_slotmem_shm.so -LoadModule headers_module /usr/lib64/apache2/mod_headers.so -LoadModule authn_core_module /usr/lib64/apache2/mod_authn_core.so -LoadModule auth_basic_module /usr/lib64/apache2/mod_auth_basic.so -LoadModule authn_file_module /usr/lib64/apache2/mod_authn_file.so -LoadModule authz_user_module /usr/lib64/apache2/mod_authz_user.so -LoadModule access_compat_module /usr/lib64/apache2/mod_access_compat.so +LoadModule log_config_module /etc/httpd/modules/mod_log_config.so +LoadModule mime_module /etc/httpd/modules/mod_mime.so +LoadModule dir_module /etc/httpd/modules/mod_dir.so +LoadModule authz_core_module /etc/httpd/modules/mod_authz_core.so +LoadModule unixd_module /etc/httpd/modules/mod_unixd.so +LoadModule mpm_event_module /etc/httpd/modules/mod_mpm_event.so +LoadModule ssl_module /etc/httpd/modules/mod_ssl.so +LoadModule env_module /etc/httpd/modules/mod_env.so +LoadModule proxy_module /etc/httpd/modules/mod_proxy.so +LoadModule proxy_ajp_module /etc/httpd/modules/mod_proxy_ajp.so +LoadModule proxy_balancer_module /etc/httpd/modules/mod_proxy_balancer.so +LoadModule proxy_http_module /etc/httpd/modules/mod_proxy_http.so +LoadModule slotmem_shm_module /etc/httpd/modules/mod_slotmem_shm.so +LoadModule headers_module /etc/httpd/modules/mod_headers.so +LoadModule authn_core_module /etc/httpd/modules/mod_authn_core.so +LoadModule auth_basic_module /etc/httpd/modules/mod_auth_basic.so +LoadModule authn_file_module /etc/httpd/modules/mod_authn_file.so +LoadModule authz_user_module /etc/httpd/modules/mod_authz_user.so +LoadModule access_compat_module /etc/httpd/modules/mod_access_compat.so diff --git a/ironic-image/ironic-config/httpd.conf.j2 b/ironic-image/ironic-config/httpd.conf.j2 index 28e5308..19cf844 100644 --- a/ironic-image/ironic-config/httpd.conf.j2 +++ b/ironic-image/ironic-config/httpd.conf.j2 @@ -1,12 +1,12 @@ -ServerRoot "/etc/httpd" +ServerRoot {{ env.HTTPD_DIR }} {%- if env.LISTEN_ALL_INTERFACES | lower == "true" %} Listen {{ env.HTTP_PORT }} {% else %} Listen {{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }} {% endif %} -Include conf.modules.d/*.conf -User ironic-suse -Group ironic-suse +Include /etc/httpd/conf.modules.d/*.conf +User apache +Group apache AllowOverride none diff --git a/ironic-image/ironic-config/inspector.ipxe.j2 b/ironic-image/ironic-config/inspector.ipxe.j2 index c105178..23b5e5e 100644 --- a/ironic-image/ironic-config/inspector.ipxe.j2 +++ b/ironic-image/ironic-config/inspector.ipxe.j2 @@ -5,6 +5,6 @@ echo In inspector.ipxe imgfree # NOTE(dtantsur): keep inspection kernel params in [mdns]params in # ironic-inspector-image and configuration in configure-ironic.sh -kernel --timeout 60000 http://{{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}/images/ironic-python-agent-${buildarch}.kernel ipa-insecure=1 ipa-inspection-collectors={{ env.IRONIC_IPA_COLLECTORS }} systemd.journald.forward_to_console=yes BOOTIF=${mac} ipa-debug=1 ipa-enable-vlan-interfaces={{ env.IRONIC_ENABLE_VLAN_INTERFACES }} ipa-inspection-dhcp-all-interfaces=1 ipa-collect-lldp=1 {{ env.INSPECTOR_EXTRA_ARGS }} initrd=ironic-python-agent.initramfs {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} || goto retry_boot -initrd --timeout 60000 http://{{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}/images/ironic-python-agent-${buildarch}.initramfs || goto retry_boot +kernel --timeout 60000 {{ env.IRONIC_HTTP_URL }}/images/ironic-python-agent.kernel ipa-insecure=1 ipa-inspection-collectors={{ env.IRONIC_IPA_COLLECTORS }} systemd.journald.forward_to_console=yes BOOTIF=${mac} ipa-debug=1 ipa-enable-vlan-interfaces={{ env.IRONIC_ENABLE_VLAN_INTERFACES }} ipa-inspection-dhcp-all-interfaces=1 ipa-collect-lldp=1 {{ env.INSPECTOR_EXTRA_ARGS }} initrd=ironic-python-agent.initramfs {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} || goto retry_boot +initrd --timeout 60000 {{ env.IRONIC_HTTP_URL }}/images/ironic-python-agent.initramfs || goto retry_boot boot diff --git a/ironic-image/ironic-config/ironic.conf.j2 b/ironic-image/ironic-config/ironic.conf.j2 index a487dc5..0442d3e 100644 --- a/ironic-image/ironic-config/ironic.conf.j2 +++ b/ironic-image/ironic-config/ironic.conf.j2 @@ -27,6 +27,7 @@ use_stderr = true hash_ring_algorithm = sha256 my_ip = {{ env.IRONIC_IP }} host = {{ env.IRONIC_CONDUCTOR_HOST }} +tempdir = {{ env.IRONIC_TMP_DATA_DIR }} # If a path to a certificate is defined, use that first for webserver {% if env.WEBSERVER_CACERT_FILE %} @@ -39,7 +40,7 @@ isolinux_bin = /usr/share/syslinux/isolinux.bin # NOTE(dtantsur): this path is specific to the GRUB image that is built into # the ESP provided in [conductor]bootloader. -grub_config_path = EFI/BOOT/grub.cfg +grub_config_path = EFI/centos/grub.cfg [agent] deploy_logs_collect = always @@ -49,6 +50,7 @@ deploy_logs_local_path = /shared/log/ironic/deploy # retries here works around such problems without affecting the normal path. # See https://bugzilla.redhat.com/show_bug.cgi?id=1822763 max_command_attempts = 30 +certificates_path = {{ env.IRONIC_GEN_CERT_DIR }} [api] {% if env.IRONIC_REVERSE_PROXY_SETUP == "true" %} @@ -71,9 +73,6 @@ enable_ssl_api = true {% endif %} api_workers = {{ env.NUMWORKERS }} -# Disable schema validation so we can pass nmstate format -network_data_schema = /etc/ironic/network-data-schema-empty.json - [conductor] automated_clean = {{ env.IRONIC_AUTOMATED_CLEAN }} # NOTE(dtantsur): keep aligned with [pxe]boot_retry_timeout below. @@ -83,7 +82,7 @@ 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 -bootloader = {{ env.IRONIC_BOOT_BASE_URL }}/uefi_esp-{{ env.DEPLOY_ARCHITECTURE }}.img +bootloader = {{ env.IRONIC_HTTP_URL }}/uefi_esp.img 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 @@ -95,16 +94,19 @@ deploy_kernel = file://{{ env.IRONIC_DEFAULT_KERNEL }} {% if env.IRONIC_DEFAULT_RAMDISK is defined %} deploy_ramdisk = file://{{ env.IRONIC_DEFAULT_RAMDISK }} {% endif %} +{% if env.DISABLE_DEEP_IMAGE_INSPECTION | lower == "true" %} +disable_deep_image_inspection = True +{% endif %} [database] -{% if env.IRONIC_USE_MARIADB | lower == "false" %} -connection = sqlite:////var/lib/ironic/ironic.sqlite +{% if env.IRONIC_USE_MARIADB | lower == "true" %} +connection = {{ env.MARIADB_CONNECTION }} +{% else %} +connection = {{ env.LOCAL_DB_URI }} # Synchronous mode is required for data integrity in case of operating system # crash. In our case we restart the container from scratch, so we can save some # IO by not doing syncs all the time. sqlite_synchronous = False -{% else %} -connection = {{ env.MARIADB_CONNECTION }} {% endif %} [deploy] @@ -112,7 +114,7 @@ default_boot_option = local erase_devices_metadata_priority = 10 erase_devices_priority = 0 http_root = /shared/html/ -http_url = {{ env.IRONIC_BOOT_BASE_URL }} +http_url = {{ env.IRONIC_HTTP_URL }} fast_track = {{ env.IRONIC_FAST_TRACK }} {% if env.IRONIC_BOOT_ISO_SOURCE %} ramdisk_image_download_source = {{ env.IRONIC_BOOT_ISO_SOURCE }} @@ -175,7 +177,7 @@ cipher_suite_versions = 3,17 # unauthenticated connections from other processes in the same host since the # containers are in host networking. auth_strategy = http_basic -http_basic_auth_user_file = /etc/ironic/htpasswd-rpc +http_basic_auth_user_file = {{ env.IRONIC_RPC_HTPASSWD_FILE }} host_ip = {% if env.LISTEN_ALL_INTERFACES | lower == "true" %}::{% else %}{{ env.IRONIC_IP }}{% endif %} {% if env.IRONIC_TLS_SETUP == "true" %} use_ssl = true @@ -187,6 +189,11 @@ insecure = {{ env.IRONIC_INSECURE }} [nova] send_power_notifications = false +[oslo_messaging_notifications] +driver = prometheus_exporter +location = /shared/ironic_prometheus_exporter +transport_url = fake:// + [pxe] # NOTE(dtantsur): keep this value at least 3x lower than # [conductor]deploy_callback_timeout so that at least some retries happen. @@ -196,7 +203,7 @@ images_path = /shared/html/tmp instance_master_path = /shared/html/master_images tftp_master_path = /shared/tftpboot/master_images tftp_root = /shared/tftpboot -kernel_append_params = nofb nomodeset vga=normal ipa-insecure={{ env.IPA_INSECURE }} {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes net.ifnames={{ '0' if env.PREDICTABLE_NIC_NAMES == 'false' else '1' }} +kernel_append_params = nofb nomodeset vga=normal ipa-insecure=1 {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes # This makes networking boot templates generated even for nodes using local # boot (the default), ensuring that they boot correctly even if they start # netbooting for some reason (e.g. with the noop management interface). @@ -209,14 +216,14 @@ ipxe_config_template = /tmp/ipxe_config.template [redfish] use_swift = false -kernel_append_params = nofb nomodeset vga=normal ipa-insecure={{ env.IPA_INSECURE }} {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes net.ifnames={{ '0' if env.PREDICTABLE_NIC_NAMES == 'false' else '1' }} +kernel_append_params = nofb nomodeset vga=normal ipa-insecure=1 {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes [ilo] -kernel_append_params = nofb nomodeset vga=normal ipa-insecure={{ env.IPA_INSECURE }} {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes net.ifnames={{ '0' if env.PREDICTABLE_NIC_NAMES == 'false' else '1' }} +kernel_append_params = nofb nomodeset vga=normal ipa-insecure=1 {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes use_web_server_for_images = true [irmc] -kernel_append_params = nofb nomodeset vga=normal ipa-insecure={{ env.IPA_INSECURE }} {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes net.ifnames={{ '0' if env.PREDICTABLE_NIC_NAMES == 'false' else '1' }} +kernel_append_params = nofb nomodeset vga=normal ipa-insecure=1 {% if env.ENABLE_FIPS_IPA %}fips={{ env.ENABLE_FIPS_IPA|trim }}{% endif %} {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} systemd.journald.forward_to_console=yes [service_catalog] endpoint_override = {{ env.IRONIC_BASE_URL }} diff --git a/ironic-image/ironic-config/network-data-schema-empty.json b/ironic-image/ironic-config/network-data-schema-empty.json deleted file mode 100644 index d31a3bc..0000000 --- a/ironic-image/ironic-config/network-data-schema-empty.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/ironic-image/scripts/auth-common.sh b/ironic-image/scripts/auth-common.sh index cb6a548..b9526d4 100644 --- a/ironic-image/scripts/auth-common.sh +++ b/ironic-image/scripts/auth-common.sh @@ -4,6 +4,12 @@ set -euxo pipefail export IRONIC_REVERSE_PROXY_SETUP=${IRONIC_REVERSE_PROXY_SETUP:-false} +# CUSTOM_CONFIG_DIR is also managed in the ironic-common.sh, in order to +# keep auth-common and ironic-common separate (to stay consistent with the +# architecture) part of the ironic-common logic had to be duplicated +CUSTOM_CONFIG_DIR="${CUSTOM_CONFIG_DIR:-/conf}" +IRONIC_CONF_DIR="${CUSTOM_CONFIG_DIR}/ironic" + # Backward compatibility if [[ "${IRONIC_DEPLOYMENT:-}" == "Conductor" ]]; then export IRONIC_EXPOSE_JSON_RPC=true @@ -11,42 +17,74 @@ else export IRONIC_EXPOSE_JSON_RPC="${IRONIC_EXPOSE_JSON_RPC:-false}" fi -IRONIC_HTPASSWD_FILE=/etc/ironic/htpasswd +IRONIC_HTPASSWD_FILE="${IRONIC_CONF_DIR}/htpasswd" +export IRONIC_RPC_HTPASSWD_FILE="${IRONIC_HTPASSWD_FILE}-rpc" if [[ -f "/auth/ironic/htpasswd" ]]; then IRONIC_HTPASSWD=$( "${IRONIC_HTPASSWD_FILE}-rpc" + else + printf "%s\n" "${IRONIC_RPC_HTPASSWD}" > "${IRONIC_RPC_HTPASSWD_FILE}" fi } configure_ironic_auth() { - local config=/etc/ironic/ironic.conf # Configure HTTP basic auth for API server if [[ -n "${IRONIC_HTPASSWD}" ]]; then printf "%s\n" "${IRONIC_HTPASSWD}" > "${IRONIC_HTPASSWD_FILE}" if [[ "${IRONIC_REVERSE_PROXY_SETUP}" == "false" ]]; then - crudini --set "${config}" DEFAULT auth_strategy http_basic - crudini --set "${config}" DEFAULT http_basic_auth_user_file "${IRONIC_HTPASSWD_FILE}" + crudini --set "${IRONIC_CONFIG}" DEFAULT auth_strategy http_basic + crudini --set "${IRONIC_CONFIG}" DEFAULT http_basic_auth_user_file "${IRONIC_HTPASSWD_FILE}" fi fi } diff --git a/ironic-image/scripts/configure-ironic.sh b/ironic-image/scripts/configure-ironic.sh old mode 100644 new mode 100755 index e2daa7d..d2154d0 --- a/ironic-image/scripts/configure-ironic.sh +++ b/ironic-image/scripts/configure-ironic.sh @@ -19,10 +19,11 @@ export IRONIC_ENABLE_VLAN_INTERFACES=${IRONIC_ENABLE_VLAN_INTERFACES:-${IRONIC_I export HTTP_PORT=${HTTP_PORT:-80} -export IRONIC_USE_MARIADB=${IRONIC_USE_MARIADB:-true} - -if [[ "$IRONIC_USE_MARIADB" == "true" ]]; then - MARIADB_PASSWORD=${MARIADB_PASSWORD} +if [[ "${IRONIC_USE_MARIADB}" == true ]]; then + if [[ -z "${MARIADB_PASSWORD:-}" ]]; then + echo "FATAL: IRONIC_USE_MARIADB requires password, mount a secret under /auth/mariadb" + exit 1 + fi MARIADB_DATABASE=${MARIADB_DATABASE:-ironic} MARIADB_USER=${MARIADB_USER:-ironic} MARIADB_HOST=${MARIADB_HOST:-127.0.0.1} @@ -32,13 +33,9 @@ if [[ "$IRONIC_USE_MARIADB" == "true" ]]; then fi fi -# TODO(dtantsur): remove the explicit default once we get -# https://review.opendev.org/761185 in the repositories -NUMPROC="$(grep -c "^processor" /proc/cpuinfo)" -if [[ "$NUMPROC" -lt 4 ]]; then - NUMPROC=4 -fi -export NUMWORKERS=${NUMWORKERS:-$NUMPROC} +# zero makes it do cpu number detection on Ironic side +export NUMWORKERS=${NUMWORKERS:-0} + # Whether to enable fast_track provisioning or not export IRONIC_FAST_TRACK=${IRONIC_FAST_TRACK:-true} @@ -57,8 +54,6 @@ wait_for_interface_or_ip # Hostname to use for the current conductor instance. export IRONIC_CONDUCTOR_HOST=${IRONIC_CONDUCTOR_HOST:-${IRONIC_URL_HOST}} -export IRONIC_BASE_URL=${IRONIC_BASE_URL:-"${IRONIC_SCHEME}://${IRONIC_URL_HOST}:${IRONIC_ACCESS_PORT}"} - if [[ -n "$IRONIC_EXTERNAL_IP" ]]; then export IRONIC_EXTERNAL_CALLBACK_URL=${IRONIC_EXTERNAL_CALLBACK_URL:-"${IRONIC_SCHEME}://${IRONIC_EXTERNAL_IP}:${IRONIC_ACCESS_PORT}"} if [[ "$IRONIC_VMEDIA_TLS_SETUP" == "true" ]]; then @@ -68,15 +63,15 @@ if [[ -n "$IRONIC_EXTERNAL_IP" ]]; then fi fi -IMAGE_CACHE_PREFIX="/shared/html/images/ironic-python-agent-${DEPLOY_ARCHITECTURE}" +IMAGE_CACHE_PREFIX=/shared/html/images/ironic-python-agent 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" fi -if [[ -f /etc/ironic/ironic.conf ]]; then +if [[ -f "${IRONIC_CONF_DIR}/ironic.conf" ]]; then # Make a copy of the original supposed empty configuration file - cp /etc/ironic/ironic.conf /etc/ironic/ironic.conf_orig + cp "${IRONIC_CONF_DIR}/ironic.conf" "${IRONIC_CONF_DIR}/ironic.conf.orig" fi # oslo.config also supports Config Opts From Environment, log them to stdout @@ -84,8 +79,7 @@ echo 'Options set from Environment variables' env | grep "^OS_" || true mkdir -p /shared/html - -configure_json_rpc_auth +mkdir -p /shared/ironic_prometheus_exporter if [[ -f /proc/sys/crypto/fips_enabled ]]; then ENABLE_FIPS_IPA=$(cat /proc/sys/crypto/fips_enabled) @@ -93,26 +87,10 @@ if [[ -f /proc/sys/crypto/fips_enabled ]]; then fi # The original ironic.conf is empty, and can be found in ironic.conf_orig -render_j2_config /etc/ironic/ironic.conf.j2 /etc/ironic/ironic.conf +render_j2_config "/etc/ironic/ironic.conf.j2" \ + "${IRONIC_CONF_DIR}/ironic.conf" -configure_client_basic_auth ironic-rpc +configure_json_rpc_auth # Make sure ironic traffic bypasses any proxies export NO_PROXY="${NO_PROXY:-},$IRONIC_IP" - -PROBE_CURL_ARGS= -if [[ "${IRONIC_REVERSE_PROXY_SETUP}" == "true" ]]; then - if [[ "${IRONIC_PRIVATE_PORT}" == "unix" ]]; then - PROBE_URL="http://127.0.0.1:6385" - PROBE_CURL_ARGS="--unix-socket /shared/ironic.sock" - else - PROBE_URL="http://127.0.0.1:${IRONIC_PRIVATE_PORT}" - fi -else - PROBE_URL="${IRONIC_BASE_URL}" -fi -export PROBE_CURL_ARGS -export PROBE_URL - -PROBE_KIND=readiness render_j2_config /bin/ironic-probe.j2 /bin/ironic-readiness -PROBE_KIND=liveness render_j2_config /bin/ironic-probe.j2 /bin/ironic-liveness diff --git a/ironic-image/scripts/ironic-common.sh b/ironic-image/scripts/ironic-common.sh index fd740f3..43b50f0 100644 --- a/ironic-image/scripts/ironic-common.sh +++ b/ironic-image/scripts/ironic-common.sh @@ -2,11 +2,36 @@ set -euxo pipefail -IRONIC_IP="${IRONIC_IP:-}" +# Export IRONIC_IP to avoid needing to lean on IRONIC_URL_HOST for consumption in +# e.g. dnsmasq configuration +export IRONIC_IP="${IRONIC_IP:-}" PROVISIONING_INTERFACE="${PROVISIONING_INTERFACE:-}" PROVISIONING_IP="${PROVISIONING_IP:-}" PROVISIONING_MACS="${PROVISIONING_MACS:-}" IPXE_CUSTOM_FIRMWARE_DIR="${IPXE_CUSTOM_FIRMWARE_DIR:-/shared/custom_ipxe_firmware}" +CUSTOM_CONFIG_DIR="${CUSTOM_CONFIG_DIR:-/conf}" +CUSTOM_DATA_DIR="${CUSTOM_DATA_DIR:-/data}" +export DNSMASQ_CONF_DIR="${CUSTOM_CONFIG_DIR}/dnsmasq" +export DNSMASQ_DATA_DIR="${CUSTOM_DATA_DIR}/dnsmasq" +export DNSMASQ_TEMP_DIR="${CUSTOM_CONFIG_DIR}/dnsmasq" +export HTTPD_DIR="${CUSTOM_CONFIG_DIR}/httpd" +export HTTPD_CONF_DIR="${HTTPD_DIR}/conf" +export HTTPD_CONF_DIR_D="${HTTPD_DIR}/conf.d" +export IRONIC_CONF_DIR="${CUSTOM_CONFIG_DIR}/ironic" +export IRONIC_DB_DIR="${CUSTOM_DATA_DIR}/db" +export IRONIC_GEN_CERT_DIR="${CUSTOM_DATA_DIR}/auto_gen_certs" +export IRONIC_TMP_DATA_DIR="${CUSTOM_DATA_DIR}/tmp" +export PROBE_CONF_DIR="${CUSTOM_CONFIG_DIR}/probes" + +mkdir -p "${IRONIC_CONF_DIR}" "${PROBE_CONF_DIR}" "${HTTPD_CONF_DIR}" \ + "${HTTPD_CONF_DIR_D}" "${DNSMASQ_CONF_DIR}" "${DNSMASQ_TEMP_DIR}" \ + "${IRONIC_DB_DIR}" "${IRONIC_GEN_CERT_DIR}" "${DNSMASQ_DATA_DIR}" \ + "${IRONIC_TMP_DATA_DIR}" + +export HTPASSWD_FILE="${IRONIC_CONF_DIR}/htpasswd" +export LOCAL_DB_URI="sqlite:///${IRONIC_DB_DIR}/ironic.sqlite" + +export IRONIC_USE_MARIADB=${IRONIC_USE_MARIADB:-false} get_provisioning_interface() { @@ -19,13 +44,13 @@ get_provisioning_interface() local interface="provisioning" if [[ -n "${PROVISIONING_IP}" ]]; then - if ip -br addr show | grep -qi " ${PROVISIONING_IP}/"; then + if ip -br addr show | grep -i " ${PROVISIONING_IP}/" &>/dev/null; then interface="$(ip -br addr show | grep -i " ${PROVISIONING_IP}/" | cut -f 1 -d ' ' | cut -f 1 -d '@')" fi fi for mac in ${PROVISIONING_MACS//,/ }; do - if ip -br link show up | grep -qi "$mac"; then + if ip -br link show up | grep -i "$mac" &>/dev/null; then interface="$(ip -br link show up | grep -i "$mac" | cut -f 1 -d ' ' | cut -f 1 -d '@')" break fi @@ -42,9 +67,12 @@ export LISTEN_ALL_INTERFACES="${LISTEN_ALL_INTERFACES:-true}" # Wait for the interface or IP to be up, sets $IRONIC_IP wait_for_interface_or_ip() { - # If $PROVISIONING_IP is specified, then we wait for that to become available on an interface, otherwise we look at $PROVISIONING_INTERFACE for an IP - if [[ -n "$PROVISIONING_IP" ]]; then - # Convert the address using ipcalc which strips out the subnet. For IPv6 addresses, this will give the short-form address + # If $PROVISIONING_IP is specified, then we wait for that to become + # available on an interface, otherwise we look at $PROVISIONING_INTERFACE + # for an IP + if [[ -n "${PROVISIONING_IP}" ]]; then + # Convert the address using ipcalc which strips out the subnet. + # For IPv6 addresses, this will give the short-form address IRONIC_IP="$(ipcalc "${PROVISIONING_IP}" | grep "^Address:" | awk '{print $2}')" export IRONIC_IP until grep -F " ${IRONIC_IP}/" <(ip -br addr show); do @@ -69,31 +97,37 @@ wait_for_interface_or_ip() export IPV=4 export IRONIC_URL_HOST="$IRONIC_IP" fi + + # Avoid having to construct full URL multiple times while allowing + # the override of IRONIC_HTTP_URL for environments in which IRONIC_IP + # is unreachable from hosts being provisioned. + export IRONIC_HTTP_URL="${IRONIC_HTTP_URL:-http://${IRONIC_URL_HOST}:${HTTP_PORT}}" + export IRONIC_TFTP_URL="${IRONIC_TFTP_URL:-tftp://${IRONIC_URL_HOST}}" + export IRONIC_BASE_URL=${IRONIC_BASE_URL:-"${IRONIC_SCHEME}://${IRONIC_URL_HOST}:${IRONIC_ACCESS_PORT}"} } render_j2_config() { - ls $1 # DEBUG - python3 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' < "$1" python3 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' < "$1" > "$2" - ls $2 # DEBUG } run_ironic_dbsync() { - if [[ "${IRONIC_USE_MARIADB:-true}" == "true" ]]; then + if [[ "${IRONIC_USE_MARIADB}" == "true" ]]; then # It's possible for the dbsync to fail if mariadb is not up yet, so # retry until success - until ironic-dbsync --config-file /etc/ironic/ironic.conf upgrade; do + until ironic-dbsync --config-file "${IRONIC_CONF_DIR}/ironic.conf" upgrade; do echo "WARNING: ironic-dbsync failed, retrying" sleep 1 done else - # SQLite does not support some statements. Fortunately, we can just create - # the schema in one go if not already created, instead of going through an upgrade - DB_VERSION="$(ironic-dbsync --config-file /etc/ironic/ironic.conf version)" + # SQLite does not support some statements. Fortunately, we can just + # create the schema in one go if not already created, instead of going + # through an upgrade + cp "/var/lib/ironic/ironic.sqlite" "${IRONIC_DB_DIR}/ironic.sqlite" + DB_VERSION="$(ironic-dbsync --config-file "${IRONIC_CONF_DIR}/ironic.conf" version)" if [[ "${DB_VERSION}" == "None" ]]; then - ironic-dbsync --config-file /etc/ironic/ironic.conf create_schema + ironic-dbsync --config-file "${IRONIC_CONF_DIR}/ironic.conf" create_schema fi fi } diff --git a/ironic-image/scripts/ironic-probe.j2 b/ironic-image/scripts/ironic-probe.j2 deleted file mode 100644 index 85a5ca7..0000000 --- a/ironic-image/scripts/ironic-probe.j2 +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -eu -o pipefail - -curl -sSf {{ env.PROBE_CURL_ARGS }} "{{ env.PROBE_URL }}" - -# TODO(dtantsur): when PROBE_KIND==readiness, try the conductor and driver API -# to make sure the conductor is ready. This requires having access to secrets -# since these endpoints are authenticated. diff --git a/ironic-image/scripts/ironic-probe.sh b/ironic-image/scripts/ironic-probe.sh new file mode 100755 index 0000000..defe5c8 --- /dev/null +++ b/ironic-image/scripts/ironic-probe.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -eu -o pipefail + +# shellcheck disable=SC1091 +. /bin/ironic-common.sh +# shellcheck disable=SC1091 +. /bin/auth-common.sh + +PROBE_CURL_ARGS= +if [[ "${IRONIC_REVERSE_PROXY_SETUP}" == "true" ]]; then + if [[ "${IRONIC_PRIVATE_PORT}" == "unix" ]]; then + PROBE_URL="http://127.0.0.1:6385" + PROBE_CURL_ARGS="--unix-socket /shared/ironic.sock" + else + PROBE_URL="http://127.0.0.1:${IRONIC_PRIVATE_PORT}" + fi +else + PROBE_URL="${IRONIC_BASE_URL}" +fi + +# shellcheck disable=SC2086 +curl -sSf ${PROBE_CURL_ARGS} "${PROBE_URL}" diff --git a/ironic-image/scripts/rundatabase-upgrade b/ironic-image/scripts/rundatabase-upgrade new file mode 100755 index 0000000..26fb1f4 --- /dev/null +++ b/ironic-image/scripts/rundatabase-upgrade @@ -0,0 +1,10 @@ +#!/usr/bin/bash + +set -euxo pipefail + +# shellcheck disable=SC1091 +. /bin/configure-ironic.sh + +# NOTE(dtantsur): no retries here: this script is supposed to be run as a Job +# that is retried on failure. +exec ironic-dbsync --config-file "${IRONIC_CONF_DIR}/ironic.conf" upgrade diff --git a/ironic-image/scripts/rundnsmasq b/ironic-image/scripts/rundnsmasq old mode 100644 new mode 100755 index 16f4c76..ba48665 --- a/ironic-image/scripts/rundnsmasq +++ b/ironic-image/scripts/rundnsmasq @@ -13,7 +13,11 @@ export DNS_PORT=${DNS_PORT:-0} wait_for_interface_or_ip if [[ "${DNS_IP:-}" == "provisioning" ]]; then - export DNS_IP="$IRONIC_URL_HOST" + if [[ "${IPV}" == "4" ]]; then + export DNS_IP="${IRONIC_IP}" + else + export DNS_IP="[${IRONIC_IP}]" + fi fi mkdir -p /shared/tftpboot @@ -32,12 +36,12 @@ fi # Template and write dnsmasq.conf # we template via /tmp as sed otherwise creates temp files in /etc directory # where we can't write -python3 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' /tmp/dnsmasq.conf +python3 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' <"/tmp/dnsmasq.conf.j2" >"${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" for iface in $(echo "$DNSMASQ_EXCEPT_INTERFACE" | tr ',' ' '); do - sed -i -e "/^interface=.*/ a\except-interface=${iface}" /tmp/dnsmasq.conf + sed -i -e "/^interface=.*/ a\except-interface=${iface}" "${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" done -cat /tmp/dnsmasq.conf > /etc/dnsmasq.conf -rm /tmp/dnsmasq.conf +cat "${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" > "${DNSMASQ_CONF_DIR}/dnsmasq.conf" +rm "${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" -exec /usr/sbin/dnsmasq -d -q -C /etc/dnsmasq.conf +exec /usr/sbin/dnsmasq -d -q -C "${DNSMASQ_CONF_DIR}/dnsmasq.conf" diff --git a/ironic-image/scripts/runhttpd b/ironic-image/scripts/runhttpd old mode 100644 new mode 100755 index d6175ba..a24e920 --- a/ironic-image/scripts/runhttpd +++ b/ironic-image/scripts/runhttpd @@ -28,25 +28,29 @@ wait_for_interface_or_ip mkdir -p /shared/html chmod 0777 /shared/html -IRONIC_BASE_URL="${IRONIC_SCHEME}://${IRONIC_URL_HOST}" - -INSPECTOR_EXTRA_ARGS=" ipa-inspection-callback-url=${IRONIC_BASE_URL}:${IRONIC_ACCESS_PORT}/v1/continue_inspection" +INSPECTOR_EXTRA_ARGS=" ipa-inspection-callback-url=${IRONIC_BASE_URL}/v1/continue_inspection" if [[ "$IRONIC_FAST_TRACK" == "true" ]]; then - INSPECTOR_EXTRA_ARGS+=" ipa-api-url=${IRONIC_BASE_URL}:${IRONIC_ACCESS_PORT}" + INSPECTOR_EXTRA_ARGS+=" ipa-api-url=${IRONIC_BASE_URL}" fi 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 /tmp/uefi_esp.img /shared/html/uefi_esp.img +# 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" +fi # Render the core httpd config -render_j2_config /etc/httpd/conf/httpd.conf.j2 /etc/httpd/conf/httpd.conf +render_j2_config "/etc/httpd/conf/httpd.conf.j2" \ + "${HTTPD_CONF_DIR}/httpd.conf" if [[ "$IRONIC_TLS_SETUP" == "true" ]]; then if [[ "${IRONIC_REVERSE_PROXY_SETUP}" == "true" ]]; then - render_j2_config /tmp/httpd-ironic-api.conf.j2 /etc/httpd/conf.d/ironic.conf + render_j2_config "/tmp/httpd-ironic-api.conf.j2" \ + "${HTTPD_CONF_DIR_D}/ironic.conf" fi else export IRONIC_REVERSE_PROXY_SETUP="false" # If TLS is not used, we have no reason to use the reverse proxy @@ -56,33 +60,24 @@ write_htpasswd_files # Render httpd TLS configuration for /shared/html/ if [[ "$IRONIC_VMEDIA_TLS_SETUP" == "true" ]]; then - render_j2_config /etc/httpd-vmedia.conf.j2 /etc/httpd/conf.d/vmedia.conf + render_j2_config "/tmp/httpd-vmedia.conf.j2" \ + "${HTTPD_CONF_DIR_D}/vmedia.conf" fi # Render httpd TLS configuration for /shared/html if [[ "$IPXE_TLS_SETUP" == "true" ]]; then mkdir -p /shared/html/custom-ipxe chmod 0777 /shared/html/custom-ipxe - render_j2_config "/etc/httpd-ipxe.conf.j2" "/etc/httpd/conf.d/ipxe.conf" + render_j2_config "/tmp/httpd-ipxe.conf.j2" "${HTTPD_CONF_DIR_D}/ipxe.conf" cp "${IPXE_CUSTOM_FIRMWARE_DIR}/undionly.kpxe" \ "${IPXE_CUSTOM_FIRMWARE_DIR}/snponly.efi" \ "/shared/html/custom-ipxe" fi # Set up inotify to kill the container (restart) whenever cert files for ironic api change -if [[ "$IRONIC_TLS_SETUP" == "true" ]] && [[ "${RESTART_CONTAINER_CERTIFICATE_UPDATED}" == "true" ]]; then - # shellcheck disable=SC2034 - python3.11 -m pyinotify --raw-format -e IN_DELETE_SELF -v "${IRONIC_CERT_FILE}" | while read -r file event; do - kill -WINCH $(pgrep httpd) - done & -fi +configure_restart_on_certificate_update "${IRONIC_TLS_SETUP}" httpd "${IRONIC_CERT_FILE}" # Set up inotify to kill the container (restart) whenever cert of httpd for /shared/html/ path change -if [[ "$IRONIC_VMEDIA_TLS_SETUP" == "true" ]] && [[ "${RESTART_CONTAINER_CERTIFICATE_UPDATED}" == "true" ]]; then - # shellcheck disable=SC2034 - python3.11 -m pyinotify --raw-format -e IN_DELETE_SELF -v "${IRONIC_VMEDIA_CERT_FILE}" | while read -r file event; do - kill -WINCH $(pgrep httpd) - done & -fi +configure_restart_on_certificate_update "${IRONIC_VMEDIA_TLS_SETUP}" httpd "${IRONIC_VMEDIA_CERT_FILE}" -exec /usr/sbin/httpd -DFOREGROUND -f /etc/httpd/conf/httpd.conf +exec /usr/sbin/httpd -DFOREGROUND -f "${HTTPD_CONF_DIR}/httpd.conf" diff --git a/ironic-image/scripts/runironic b/ironic-image/scripts/runironic old mode 100644 new mode 100755 index d782fe1..2d2b35e --- a/ironic-image/scripts/runironic +++ b/ironic-image/scripts/runironic @@ -1,23 +1,18 @@ #!/usr/bin/bash -# This setting must go before configure-ironic since it has different defaults. -export IRONIC_USE_MARIADB=${IRONIC_USE_MARIADB:-false} - # shellcheck disable=SC1091 . /bin/configure-ironic.sh # Ramdisk logs mkdir -p /shared/log/ironic/deploy -run_ironic_dbsync - -if [[ "$IRONIC_TLS_SETUP" == "true" ]] && [[ "${RESTART_CONTAINER_CERTIFICATE_UPDATED}" == "true" ]]; then - # shellcheck disable=SC2034 - python3.11 -m pyinotify --raw-format -e IN_DELETE_SELF -v "${IRONIC_CERT_FILE}" | while read -r file event; do - kill $(pgrep ironic) - done & +# Allows skipping dbsync if it's done by an external job +if [[ "${IRONIC_SKIP_DBSYNC:-false}" != true ]]; then + run_ironic_dbsync fi +configure_restart_on_certificate_update "${IRONIC_TLS_SETUP}" ironic "${IRONIC_CERT_FILE}" + configure_ironic_auth -exec /usr/bin/ironic +exec /usr/bin/ironic --config-dir "${IRONIC_CONF_DIR}" diff --git a/ironic-image/scripts/runironic-exporter b/ironic-image/scripts/runironic-exporter new file mode 100755 index 0000000..e429a45 --- /dev/null +++ b/ironic-image/scripts/runironic-exporter @@ -0,0 +1,14 @@ +#!/usr/bin/bash + +# shellcheck disable=SC1091 +. /bin/configure-ironic.sh +# shellcheck disable=SC1091 +. /bin/ironic-common.sh + +FLASK_RUN_HOST=${FLASK_RUN_HOST:-0.0.0.0} +FLASK_RUN_PORT=${FLASK_RUN_PORT:-9608} + +export IRONIC_CONFIG="${IRONIC_CONF_DIR}/ironic.conf" + +exec gunicorn -b "${FLASK_RUN_HOST}:${FLASK_RUN_PORT}" -w 4 \ + ironic_prometheus_exporter.app.wsgi:application diff --git a/ironic-image/scripts/runlogwatch.sh b/ironic-image/scripts/runlogwatch.sh old mode 100644 new mode 100755 index a2255d2..da06bf4 --- a/ironic-image/scripts/runlogwatch.sh +++ b/ironic-image/scripts/runlogwatch.sh @@ -3,17 +3,15 @@ # Ramdisk logs path LOG_DIR="/shared/log/ironic/deploy" -# The ironic container creates the directory, wait for -# it to exist before running inotifywait or it can fail causing -# a spurious restart -while [ ! -d "${LOG_DIR}" ]; do - echo "Waiting for ${LOG_DIR}" - sleep 5 -done +mkdir -p "${LOG_DIR}" -python3.11 -m pyinotify --raw-format -e IN_CLOSE_WRITE -v "${LOG_DIR}" | - while read -r path _action file; do - echo "************ Contents of ${path}/${file} ramdisk log file bundle **************" - tar -xOzvvf "${path}/${file}" | sed -e "s/^/${file}: /" - rm -f "${path}/${file}" +# shellcheck disable=SC2034 +python3 -m pyinotify --raw-format -e IN_CLOSE_WRITE -v "${LOG_DIR}" | + while read -r event dir mask maskname filename filepath pathname wd; do + #NOTE(elfosardo): a pyinotify event looks like this: + # + FILENAME=$(echo "${filename}" | cut -d'=' -f2-) + echo "************ Contents of ${LOG_DIR}/${FILENAME} ramdisk log file bundle **************" + tar -xOzvvf "${LOG_DIR}/${FILENAME}" | sed -e "s/^/${FILENAME}: /" + rm -f "${LOG_DIR}/${FILENAME}" done diff --git a/ironic-image/scripts/runonline-data-migrations b/ironic-image/scripts/runonline-data-migrations new file mode 100755 index 0000000..14383c4 --- /dev/null +++ b/ironic-image/scripts/runonline-data-migrations @@ -0,0 +1,10 @@ +#!/usr/bin/bash + +set -euxo pipefail + +# shellcheck disable=SC1091 +. /bin/configure-ironic.sh + +# NOTE(dtantsur): no retries here: this script is supposed to be run as a Job +# that is retried on failure. +exec ironic-dbsync --config-file "${IRONIC_CONF_DIR}/ironic.conf" online_data_migrations diff --git a/ironic-image/scripts/tls-common.sh b/ironic-image/scripts/tls-common.sh index 6805885..1229715 100644 --- a/ironic-image/scripts/tls-common.sh +++ b/ironic-image/scripts/tls-common.sh @@ -95,3 +95,21 @@ if [[ -f "$MARIADB_CACERT_FILE" ]]; then else export MARIADB_TLS_ENABLED="false" fi + +configure_restart_on_certificate_update() +{ + local enabled="$1" + local service="$2" + local cert_file="$3" + local signal="TERM" + + if [[ "${enabled}" == "true" ]] && [[ "${RESTART_CONTAINER_CERTIFICATE_UPDATED}" == "true" ]]; then + if [[ "${service}" == httpd ]]; then + signal="WINCH" + fi + python3 -m pyinotify --raw-format -e IN_DELETE_SELF -v "${cert_file}" | + while read -r; do + pkill "-${signal}" "${service}" + done & + fi +} -- 2.49.0 From df4fce438810e020e462833b57139ec9488f7cf39d3c1e6d96e21348c738423a Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 26 Jun 2025 13:17:22 +0000 Subject: [PATCH 02/15] Use correct paths and modules for Apache Correct the path of the Apache modules for a SUSE image. Also keep a couple of modules disabled. Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/httpd-modules.conf | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/ironic-image/ironic-config/httpd-modules.conf b/ironic-image/ironic-config/httpd-modules.conf index 5984994..fd2bc99 100644 --- a/ironic-image/ironic-config/httpd-modules.conf +++ b/ironic-image/ironic-config/httpd-modules.conf @@ -1,20 +1,20 @@ # Bare minimum set of modules -LoadModule log_config_module /etc/httpd/modules/mod_log_config.so -LoadModule mime_module /etc/httpd/modules/mod_mime.so -LoadModule dir_module /etc/httpd/modules/mod_dir.so -LoadModule authz_core_module /etc/httpd/modules/mod_authz_core.so -LoadModule unixd_module /etc/httpd/modules/mod_unixd.so -LoadModule mpm_event_module /etc/httpd/modules/mod_mpm_event.so -LoadModule ssl_module /etc/httpd/modules/mod_ssl.so -LoadModule env_module /etc/httpd/modules/mod_env.so -LoadModule proxy_module /etc/httpd/modules/mod_proxy.so -LoadModule proxy_ajp_module /etc/httpd/modules/mod_proxy_ajp.so -LoadModule proxy_balancer_module /etc/httpd/modules/mod_proxy_balancer.so -LoadModule proxy_http_module /etc/httpd/modules/mod_proxy_http.so -LoadModule slotmem_shm_module /etc/httpd/modules/mod_slotmem_shm.so -LoadModule headers_module /etc/httpd/modules/mod_headers.so -LoadModule authn_core_module /etc/httpd/modules/mod_authn_core.so -LoadModule auth_basic_module /etc/httpd/modules/mod_auth_basic.so -LoadModule authn_file_module /etc/httpd/modules/mod_authn_file.so -LoadModule authz_user_module /etc/httpd/modules/mod_authz_user.so -LoadModule access_compat_module /etc/httpd/modules/mod_access_compat.so +LoadModule log_config_module /usr/lib64/apache2/mod_log_config.so +LoadModule mime_module /usr/lib64/apache2/mod_mime.so +LoadModule dir_module /usr/lib64/apache2/mod_dir.so +LoadModule authz_core_module /usr/lib64/apache2/mod_authz_core.so +#LoadModule unixd_module modules/mod_unixd.so +#LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule ssl_module /usr/lib64/apache2/mod_ssl.so +LoadModule env_module /usr/lib64/apache2/mod_env.so +LoadModule proxy_module /usr/lib64/apache2/mod_proxy.so +LoadModule proxy_ajp_module /usr/lib64/apache2/mod_proxy_ajp.so +LoadModule proxy_balancer_module /usr/lib64/apache2/mod_proxy_balancer.so +LoadModule proxy_http_module /usr/lib64/apache2/mod_proxy_http.so +LoadModule slotmem_shm_module /usr/lib64/apache2/mod_slotmem_shm.so +LoadModule headers_module /usr/lib64/apache2/mod_headers.so +LoadModule authn_core_module /usr/lib64/apache2/mod_authn_core.so +LoadModule auth_basic_module /usr/lib64/apache2/mod_auth_basic.so +LoadModule authn_file_module /usr/lib64/apache2/mod_authn_file.so +LoadModule authz_user_module /usr/lib64/apache2/mod_authz_user.so +LoadModule access_compat_module /usr/lib64/apache2/mod_access_compat.so -- 2.49.0 From cc08766fdf2f23afe3509397271c0d281840dabc7bb8b89e80b0a560196ad11f Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 26 Jun 2025 13:32:48 +0000 Subject: [PATCH 03/15] Use ironic-suse user/group in Apache for media Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/httpd.conf.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ironic-image/ironic-config/httpd.conf.j2 b/ironic-image/ironic-config/httpd.conf.j2 index 19cf844..ef3de62 100644 --- a/ironic-image/ironic-config/httpd.conf.j2 +++ b/ironic-image/ironic-config/httpd.conf.j2 @@ -5,8 +5,8 @@ Listen {{ env.HTTP_PORT }} Listen {{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }} {% endif %} Include /etc/httpd/conf.modules.d/*.conf -User apache -Group apache +User ironic-suse +Group ironic-suse AllowOverride none -- 2.49.0 From dc68a1d44b88c769e5e68017c924f0a92452f8c7def9f6c9557ad2aa153890d9 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 26 Jun 2025 13:14:07 +0000 Subject: [PATCH 04/15] Use ironic-suse user/group in Apache for API Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/httpd-ironic-api.conf.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ironic-image/ironic-config/httpd-ironic-api.conf.j2 b/ironic-image/ironic-config/httpd-ironic-api.conf.j2 index a374894..317118e 100644 --- a/ironic-image/ironic-config/httpd-ironic-api.conf.j2 +++ b/ironic-image/ironic-config/httpd-ironic-api.conf.j2 @@ -27,8 +27,8 @@ Listen {{ env.IRONIC_URL_HOST }}:{{ env.IRONIC_LISTEN_PORT }} ProxyPassReverse "/" "http://127.0.0.1:{{ env.IRONIC_PRIVATE_PORT }}/" {% endif %} - SetEnv APACHE_RUN_USER ironic - SetEnv APACHE_RUN_GROUP ironic + SetEnv APACHE_RUN_USER ironic-suse + SetEnv APACHE_RUN_GROUP ironic-suse ErrorLog /dev/stderr LogLevel debug -- 2.49.0 From 37a92b02c48286f6c37d3cb9a6c2e7eecab937de8d905e6360a067d4462b03d4 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Mon, 30 Jun 2025 11:43:07 +0000 Subject: [PATCH 05/15] Change GRUB path in ironic.conf Correct path for grub.cfg on a SUSE system. Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/ironic.conf.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic-image/ironic-config/ironic.conf.j2 b/ironic-image/ironic-config/ironic.conf.j2 index 0442d3e..e7def4e 100644 --- a/ironic-image/ironic-config/ironic.conf.j2 +++ b/ironic-image/ironic-config/ironic.conf.j2 @@ -40,7 +40,7 @@ isolinux_bin = /usr/share/syslinux/isolinux.bin # NOTE(dtantsur): this path is specific to the GRUB image that is built into # the ESP provided in [conductor]bootloader. -grub_config_path = EFI/centos/grub.cfg +grub_config_path = EFI/BOOT/grub.cfg [agent] deploy_logs_collect = always -- 2.49.0 From db28299e63438360ed20950bd017c2566e25dde316898bceea1d87cad55713a9 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 26 Jun 2025 14:06:13 +0000 Subject: [PATCH 06/15] Use arch named IPA file in IMAGE_CACHE_PREFIX Signed-off-by: Marco Chiappero --- ironic-image/scripts/configure-ironic.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic-image/scripts/configure-ironic.sh b/ironic-image/scripts/configure-ironic.sh index d2154d0..f7c3c21 100755 --- a/ironic-image/scripts/configure-ironic.sh +++ b/ironic-image/scripts/configure-ironic.sh @@ -63,7 +63,7 @@ if [[ -n "$IRONIC_EXTERNAL_IP" ]]; then fi fi -IMAGE_CACHE_PREFIX=/shared/html/images/ironic-python-agent +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" -- 2.49.0 From 728354e926919e561b9425958143a9151c1561481ba64735c7c10f4cee93dd95 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 26 Jun 2025 13:36:24 +0000 Subject: [PATCH 07/15] Use arch specific file for IPA Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/inspector.ipxe.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ironic-image/ironic-config/inspector.ipxe.j2 b/ironic-image/ironic-config/inspector.ipxe.j2 index 23b5e5e..80ff90d 100644 --- a/ironic-image/ironic-config/inspector.ipxe.j2 +++ b/ironic-image/ironic-config/inspector.ipxe.j2 @@ -5,6 +5,6 @@ echo In inspector.ipxe imgfree # NOTE(dtantsur): keep inspection kernel params in [mdns]params in # ironic-inspector-image and configuration in configure-ironic.sh -kernel --timeout 60000 {{ env.IRONIC_HTTP_URL }}/images/ironic-python-agent.kernel ipa-insecure=1 ipa-inspection-collectors={{ env.IRONIC_IPA_COLLECTORS }} systemd.journald.forward_to_console=yes BOOTIF=${mac} ipa-debug=1 ipa-enable-vlan-interfaces={{ env.IRONIC_ENABLE_VLAN_INTERFACES }} ipa-inspection-dhcp-all-interfaces=1 ipa-collect-lldp=1 {{ env.INSPECTOR_EXTRA_ARGS }} initrd=ironic-python-agent.initramfs {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} || goto retry_boot -initrd --timeout 60000 {{ env.IRONIC_HTTP_URL }}/images/ironic-python-agent.initramfs || goto retry_boot +kernel --timeout 60000 {{ env.IRONIC_HTTP_URL }}/images/ironic-python-agent-${buildarch}.kernel ipa-insecure=1 ipa-inspection-collectors={{ env.IRONIC_IPA_COLLECTORS }} systemd.journald.forward_to_console=yes BOOTIF=${mac} ipa-debug=1 ipa-enable-vlan-interfaces={{ env.IRONIC_ENABLE_VLAN_INTERFACES }} ipa-inspection-dhcp-all-interfaces=1 ipa-collect-lldp=1 {{ env.INSPECTOR_EXTRA_ARGS }} initrd=ironic-python-agent-${buildarch}.initramfs {% if env.IRONIC_RAMDISK_SSH_KEY %}sshkey="{{ env.IRONIC_RAMDISK_SSH_KEY|trim }}"{% endif %} {{ env.IRONIC_KERNEL_PARAMS|trim }} || goto retry_boot +initrd --timeout 60000 {{ env.IRONIC_HTTP_URL }}/images/ironic-python-agent-${buildarch}.initramfs || goto retry_boot boot -- 2.49.0 From 589be0a21f3c6fafd1ea7000d34f2ef991b6bc92db5c8b70d9e21a7b059b7e67 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Tue, 8 Jul 2025 08:26:07 +0000 Subject: [PATCH 08/15] Use arch specific ESP img Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/ironic.conf.j2 | 2 +- ironic-image/scripts/runhttpd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ironic-image/ironic-config/ironic.conf.j2 b/ironic-image/ironic-config/ironic.conf.j2 index e7def4e..145eed1 100644 --- a/ironic-image/ironic-config/ironic.conf.j2 +++ b/ironic-image/ironic-config/ironic.conf.j2 @@ -82,7 +82,7 @@ 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 -bootloader = {{ env.IRONIC_HTTP_URL }}/uefi_esp.img +bootloader = {{ env.IRONIC_HTTP_URL }}/uefi_esp-{{ env.DEPLOY_ARCHITECTURE }}.img 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 diff --git a/ironic-image/scripts/runhttpd b/ironic-image/scripts/runhttpd index a24e920..97208cd 100755 --- a/ironic-image/scripts/runhttpd +++ b/ironic-image/scripts/runhttpd @@ -37,7 +37,7 @@ 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/uefi_esp.img +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" -- 2.49.0 From 909aa27611e2cb6ed311a4eaf1d69f6f5ce9274aac924db728308382c3f5e435 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Fri, 4 Jul 2025 15:10:00 +0000 Subject: [PATCH 09/15] Disable the network schema check to allow for nmstate definitions Bypass the OpenStack network-data format validation, to allow for the nmstate based one we instead use (which would otherwise fail). Signed-off-by: Marco Chiappero --- ironic-image/Dockerfile | 3 +-- ironic-image/ironic-config/ironic.conf.j2 | 3 +++ ironic-image/ironic-config/network-data-schema-empty.json | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 ironic-image/ironic-config/network-data-schema-empty.json diff --git a/ironic-image/Dockerfile b/ironic-image/Dockerfile index 00d40b1..00d0fe6 100644 --- a/ironic-image/Dockerfile +++ b/ironic-image/Dockerfile @@ -88,8 +88,7 @@ RUN if [ "$(uname -m)" = "aarch64" ]; then\ 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 ironic-config/ironic.conf.j2 /etc/ironic/ -COPY ironic-config/network-data-schema-empty.json /etc/ironic/ +COPY ironic-config/ironic.conf.j2 ironic-config/network-data-schema-empty.json /etc/ironic/ # DNSMASQ COPY ironic-config/dnsmasq.conf.j2 /etc/ diff --git a/ironic-image/ironic-config/ironic.conf.j2 b/ironic-image/ironic-config/ironic.conf.j2 index 145eed1..6fd152a 100644 --- a/ironic-image/ironic-config/ironic.conf.j2 +++ b/ironic-image/ironic-config/ironic.conf.j2 @@ -73,6 +73,9 @@ enable_ssl_api = true {% endif %} api_workers = {{ env.NUMWORKERS }} +# Disable schema validation so we can pass nmstate format +network_data_schema = /etc/ironic/network-data-schema-empty.json + [conductor] automated_clean = {{ env.IRONIC_AUTOMATED_CLEAN }} # NOTE(dtantsur): keep aligned with [pxe]boot_retry_timeout below. diff --git a/ironic-image/ironic-config/network-data-schema-empty.json b/ironic-image/ironic-config/network-data-schema-empty.json new file mode 100644 index 0000000..d31a3bc --- /dev/null +++ b/ironic-image/ironic-config/network-data-schema-empty.json @@ -0,0 +1 @@ +{} -- 2.49.0 From 7da89fbe2bc596e410c44c84d22761a63e1354e9237b0248384d6292b0b810b5 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Mon, 30 Jun 2025 12:33:42 +0000 Subject: [PATCH 10/15] Update the destination path of Jinjia templates Previously .j2 files used to be copied to /etc before being instantiated. In order to make the image potentially read only, move the templates to /tmp. Signed-off-by: Marco Chiappero --- ironic-image/Dockerfile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ironic-image/Dockerfile b/ironic-image/Dockerfile index 00d0fe6..517f876 100644 --- a/ironic-image/Dockerfile +++ b/ironic-image/Dockerfile @@ -72,7 +72,8 @@ COPY configure-nonroot.sh /bin/ RUN set -euo pipefail; chmod +x /bin/configure-ironic.sh /bin/rundnsmasq /bin/runhttpd /bin/runironic /bin/runlogwatch.sh /bin/configure-nonroot.sh COPY ironic-config/inspector.ipxe.j2 ironic-config/httpd-ironic-api.conf.j2 \ - ironic-config/ipxe_config.template /tmp/ + ironic-config/ipxe_config.template ironic-config/dnsmasq.conf.j2 \ + /tmp/ # IRONIC # RUN cp /usr/share/ipxe/undionly.kpxe /tftpboot/undionly.kpxe @@ -90,9 +91,6 @@ COPY --from=base /tmp/esp-aarch64.img /tmp/uefi_esp-arm64.img COPY ironic-config/ironic.conf.j2 ironic-config/network-data-schema-empty.json /etc/ironic/ -# DNSMASQ -COPY ironic-config/dnsmasq.conf.j2 /etc/ - # Workaround # Removing the 010-ironic.conf file that comes with the package RUN rm /etc/ironic/ironic.conf.d/010-ironic.conf @@ -100,8 +98,8 @@ RUN rm /etc/ironic/ironic.conf.d/010-ironic.conf # Custom httpd config, removes all but the bare minimum needed modules COPY ironic-config/httpd.conf.j2 /etc/httpd/conf/ COPY ironic-config/httpd-modules.conf /etc/httpd/conf.modules.d/ -COPY ironic-config/apache2-vmedia.conf.j2 /etc/httpd-vmedia.conf.j2 -COPY ironic-config/apache2-ipxe.conf.j2 /etc/httpd-ipxe.conf.j2 +COPY ironic-config/apache2-vmedia.conf.j2 /tmp/httpd-vmedia.conf.j2 +COPY ironic-config/apache2-ipxe.conf.j2 /tmp/httpd-ipxe.conf.j2 # configure non-root user and set relevant permissions RUN configure-nonroot.sh && rm -f /bin/configure-nonroot.sh -- 2.49.0 From 58f9552a263881c8104239c29f9d573f00928338732fe08e05529fe58e94d7d0 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 26 Jun 2025 14:09:46 +0000 Subject: [PATCH 11/15] Force the use of Python 3.11 SLE 15.6 provides Python 3.11, make sure it's enforced. Signed-off-by: Marco Chiappero --- ironic-image/scripts/ironic-common.sh | 2 +- ironic-image/scripts/rundnsmasq | 2 +- ironic-image/scripts/runlogwatch.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ironic-image/scripts/ironic-common.sh b/ironic-image/scripts/ironic-common.sh index 43b50f0..b47ff38 100644 --- a/ironic-image/scripts/ironic-common.sh +++ b/ironic-image/scripts/ironic-common.sh @@ -108,7 +108,7 @@ wait_for_interface_or_ip() render_j2_config() { - python3 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' < "$1" > "$2" + python3.11 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' < "$1" > "$2" } run_ironic_dbsync() diff --git a/ironic-image/scripts/rundnsmasq b/ironic-image/scripts/rundnsmasq index ba48665..7c5b876 100755 --- a/ironic-image/scripts/rundnsmasq +++ b/ironic-image/scripts/rundnsmasq @@ -36,7 +36,7 @@ fi # Template and write dnsmasq.conf # we template via /tmp as sed otherwise creates temp files in /etc directory # where we can't write -python3 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' <"/tmp/dnsmasq.conf.j2" >"${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" +python3.11 -c 'import os; import sys; import jinja2; sys.stdout.write(jinja2.Template(sys.stdin.read()).render(env=os.environ))' <"/tmp/dnsmasq.conf.j2" >"${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" for iface in $(echo "$DNSMASQ_EXCEPT_INTERFACE" | tr ',' ' '); do sed -i -e "/^interface=.*/ a\except-interface=${iface}" "${DNSMASQ_TEMP_DIR}/dnsmasq_temp.conf" diff --git a/ironic-image/scripts/runlogwatch.sh b/ironic-image/scripts/runlogwatch.sh index da06bf4..f1346e7 100755 --- a/ironic-image/scripts/runlogwatch.sh +++ b/ironic-image/scripts/runlogwatch.sh @@ -6,7 +6,7 @@ LOG_DIR="/shared/log/ironic/deploy" mkdir -p "${LOG_DIR}" # shellcheck disable=SC2034 -python3 -m pyinotify --raw-format -e IN_CLOSE_WRITE -v "${LOG_DIR}" | +python3.11 -m pyinotify --raw-format -e IN_CLOSE_WRITE -v "${LOG_DIR}" | while read -r event dir mask maskname filename filepath pathname wd; do #NOTE(elfosardo): a pyinotify event looks like this: # -- 2.49.0 From 16d2670d542702d8f5f8248a71795617281367110d5a14464954e2b37a43c8fd Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Fri, 4 Jul 2025 15:26:51 +0000 Subject: [PATCH 12/15] Remove unused prometheus exporter The Prometheus exporter is effectively, not only unused, but unusable, due to missing dependencies. Since currently we don't have use case for it, opt for dropping the exporter entirely from the image. Signed-off-by: Marco Chiappero --- ironic-image/ironic-config/ironic.conf.j2 | 5 ----- ironic-image/scripts/configure-ironic.sh | 1 - ironic-image/scripts/runironic-exporter | 14 -------------- 3 files changed, 20 deletions(-) delete mode 100755 ironic-image/scripts/runironic-exporter diff --git a/ironic-image/ironic-config/ironic.conf.j2 b/ironic-image/ironic-config/ironic.conf.j2 index 6fd152a..0b99531 100644 --- a/ironic-image/ironic-config/ironic.conf.j2 +++ b/ironic-image/ironic-config/ironic.conf.j2 @@ -192,11 +192,6 @@ insecure = {{ env.IRONIC_INSECURE }} [nova] send_power_notifications = false -[oslo_messaging_notifications] -driver = prometheus_exporter -location = /shared/ironic_prometheus_exporter -transport_url = fake:// - [pxe] # NOTE(dtantsur): keep this value at least 3x lower than # [conductor]deploy_callback_timeout so that at least some retries happen. diff --git a/ironic-image/scripts/configure-ironic.sh b/ironic-image/scripts/configure-ironic.sh index f7c3c21..781bf48 100755 --- a/ironic-image/scripts/configure-ironic.sh +++ b/ironic-image/scripts/configure-ironic.sh @@ -79,7 +79,6 @@ echo 'Options set from Environment variables' env | grep "^OS_" || true mkdir -p /shared/html -mkdir -p /shared/ironic_prometheus_exporter if [[ -f /proc/sys/crypto/fips_enabled ]]; then ENABLE_FIPS_IPA=$(cat /proc/sys/crypto/fips_enabled) diff --git a/ironic-image/scripts/runironic-exporter b/ironic-image/scripts/runironic-exporter deleted file mode 100755 index e429a45..0000000 --- a/ironic-image/scripts/runironic-exporter +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/bash - -# shellcheck disable=SC1091 -. /bin/configure-ironic.sh -# shellcheck disable=SC1091 -. /bin/ironic-common.sh - -FLASK_RUN_HOST=${FLASK_RUN_HOST:-0.0.0.0} -FLASK_RUN_PORT=${FLASK_RUN_PORT:-9608} - -export IRONIC_CONFIG="${IRONIC_CONF_DIR}/ironic.conf" - -exec gunicorn -b "${FLASK_RUN_HOST}:${FLASK_RUN_PORT}" -w 4 \ - ironic_prometheus_exporter.app.wsgi:application -- 2.49.0 From 5b8cbc57e14f30dbcafd260a980d75f1fb95495c69fd3ec6412ff92820d23e83 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Tue, 8 Jul 2025 10:41:02 +0000 Subject: [PATCH 13/15] Make the new scripts executable v29.0.0 add a couple of new scripts, such as ironic-probe.sh; make sure they have the 'executable' flag. Signed-off-by: Marco Chiappero --- ironic-image/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironic-image/Dockerfile b/ironic-image/Dockerfile index 517f876..189daf3 100644 --- a/ironic-image/Dockerfile +++ b/ironic-image/Dockerfile @@ -69,7 +69,7 @@ RUN mkdir -p $GRUB_DIR COPY scripts/ /bin/ COPY configure-nonroot.sh /bin/ -RUN set -euo pipefail; chmod +x /bin/configure-ironic.sh /bin/rundnsmasq /bin/runhttpd /bin/runironic /bin/runlogwatch.sh /bin/configure-nonroot.sh +RUN set -euo pipefail; chmod +x /bin/configure-ironic.sh /bin/ironic-probe.sh /bin/rundatabase-upgrade /bin/rundnsmasq /bin/runhttpd /bin/runironic /bin/runlogwatch.sh /bin/runonline-data-migrations /bin/configure-nonroot.sh COPY ironic-config/inspector.ipxe.j2 ironic-config/httpd-ironic-api.conf.j2 \ ironic-config/ipxe_config.template ironic-config/dnsmasq.conf.j2 \ -- 2.49.0 From 05aa1341a9e4621cd388515cf073ae0d29318b16fc8c432376e8b145fd3aa06b Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Wed, 9 Jul 2025 15:43:36 +0000 Subject: [PATCH 14/15] Align configure-nonroot.sh Try to reuse as much as possible of the upstream configure-nonroot.sh Co-authored-by: Nicolas Belouin Signed-off-by: Marco Chiappero --- ironic-image/configure-nonroot.sh | 55 ++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 19 deletions(-) mode change 100644 => 100755 ironic-image/configure-nonroot.sh diff --git a/ironic-image/configure-nonroot.sh b/ironic-image/configure-nonroot.sh old mode 100644 new mode 100755 index 6f07cba..8f680ab --- a/ironic-image/configure-nonroot.sh +++ b/ironic-image/configure-nonroot.sh @@ -1,53 +1,70 @@ #!/usr/bin/bash +# This script changes permissions to allow Ironic container to run as non-root +# user. As the same image is used to run ironic, ironic-httpd, ironic-dsnmasq, +# and ironic-log-watch via BMO's ironic k8s manifest, it has +# to be configured to work with multiple different users and groups, while they +# share files via bind mounts (/shared, /certs/*), which can only get one +# group id as "fsGroup". Additionally, dnsmasq needs three capabilities to run +# which we provide via "setcap", and "allowPrivilegeEscalation: true" in +# manifest. + +set -eux + +# user and group are from ironic rpms (uid 997, gid 994) NONROOT_UID=10475 NONROOT_GID=10475 -USER="ironic-suse" +IRONIC_USER="ironic-suse" +IRONIC_GROUP="ironic-suse" -groupadd -r -g ${NONROOT_GID} ${USER} +groupadd -r -g ${NONROOT_GID} ${IRONIC_GROUP} useradd -r -g ${NONROOT_GID} \ -u ${NONROOT_UID} \ -d /var/lib/ironic \ -s /sbin/nologin \ - ${USER} + ${IRONIC_USER} -# create ironic's http_root directory -mkdir -p /shared/html -chown "${NONROOT_UID}":"${NONROOT_GID}" /shared/html +# most containers mount /shared but dnsmasq can live without it +mkdir -p /shared +mkdir -p /data +mkdir -p /conf +chown "${IRONIC_USER}":"${IRONIC_GROUP}" /shared +chown "${IRONIC_USER}":"${IRONIC_GROUP}" /data +chown "${IRONIC_USER}":"${IRONIC_GROUP}" /conf # we'll bind mount shared ca and ironic certificate dirs here # that need to have correct ownership as the entire ironic in BMO # deployment shares a single fsGroup in manifest's securityContext mkdir -p /certs/ca -chown "${NONROOT_UID}":"${NONROOT_GID}" /certs{,/ca} +chown "${IRONIC_USER}":"${IRONIC_GROUP}" /certs{,/ca} chmod 2775 /certs{,/ca} # apache2 permission changes -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /etc/apache2 -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /run +chown -R "${IRONIC_USER}":"${IRONIC_GROUP}" /etc/apache2 +chown -R "${IRONIC_USER}":"${IRONIC_GROUP}" /run # ironic and httpd related changes mkdir -p /etc/httpd/conf.d -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /etc/ironic /etc/httpd /etc/httpd -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /var/log +chown -R "${IRONIC_USER}":"${IRONIC_GROUP}" /etc/ironic /etc/httpd/conf /etc/httpd/conf.d chmod 2775 /etc/ironic /etc/httpd/conf /etc/httpd/conf.d -chmod 664 /etc/ironic/* /etc/httpd/conf/* /etc/httpd/conf.d/* +#chmod 664 /etc/ironic/* /etc/httpd/conf/* /etc/httpd/conf.d/* +chmod 664 /etc/ironic/* /etc/httpd/conf/* -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /var/lib/ironic +chown -R "${IRONIC_USER}":"${IRONIC_GROUP}" /var/lib/ironic +chmod 2775 /var/lib/ironic chmod 664 /var/lib/ironic/ironic.sqlite # dnsmasq, and the capabilities required to run it as non-root user -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /etc/dnsmasq.conf /var/lib/dnsmasq -chmod 2775 /var/lib/dnsmasq -touch /var/lib/dnsmasq/dnsmasq.leases -chmod 664 /etc/dnsmasq.conf /var/lib/dnsmasq/dnsmasq.leases +chown -R "${IRONIC_USER}":"${IRONIC_GROUP}" /etc/dnsmasq.conf +#handled at chart level +#setcap "cap_net_raw,cap_net_admin,cap_net_bind_service=+eip" /usr/sbin/dnsmasq # ca-certificates permission changes touch /var/lib/ca-certificates/ca-bundle.pem.new -chown -R "${NONROOT_UID}":"${NONROOT_GID}" /var/lib/ca-certificates/ +chown -R "${IRONIC_USER}":"${IRONIC_GROUP}" /var/lib/ca-certificates/ chmod -R +w /var/lib/ca-certificates/ # probes that are created before start touch /bin/ironic-{readi,live}ness -chown root:"${NONROOT_GID}" /bin/ironic-{readi,live}ness +chown root:"${IRONIC_GROUP}" /bin/ironic-{readi,live}ness chmod 775 /bin/ironic-{readi,live}ness -- 2.49.0 From e30fc2dc3e8d76f400ac9b387e020f4ca3668cc6e37db5464e225d5c97d6a56f Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Mon, 14 Jul 2025 11:54:52 +0000 Subject: [PATCH 15/15] Bump ironic-image and metal3-chart Ironic image has been updated to v29.0.0, bump the version on the Helm Charts too. Signed-off-by: Marco Chiappero --- metal3-chart/Chart.yaml | 2 +- metal3-chart/charts/ironic/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metal3-chart/Chart.yaml b/metal3-chart/Chart.yaml index 0955e45..be6b247 100644 --- a/metal3-chart/Chart.yaml +++ b/metal3-chart/Chart.yaml @@ -10,7 +10,7 @@ dependencies: - alias: metal3-ironic name: ironic repository: file://./charts/ironic - version: 0.10.6 + version: 0.11.0 - alias: metal3-mariadb condition: global.enable_mariadb name: mariadb diff --git a/metal3-chart/charts/ironic/Chart.yaml b/metal3-chart/charts/ironic/Chart.yaml index a174d38..9afd980 100644 --- a/metal3-chart/charts/ironic/Chart.yaml +++ b/metal3-chart/charts/ironic/Chart.yaml @@ -3,4 +3,4 @@ appVersion: 26.1.2 description: A Helm chart for Ironic, used by Metal3 name: ironic type: application -version: 0.10.6 +version: 0.11.0 -- 2.49.0