1
0
forked from suse-edge/Factory

23 Commits
main ... main

Author SHA256 Message Date
5ece6cd64e Temporarily grant access to anything on HTTPS
Unfortuantely, likely due to some conflicts in the Apache, access cannot
be granted to /images/ only, so allow anyone for now.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-08 15:30:56 +00:00
0da5de1c06 Use Apache 2.4 syntax for access control on TLS HTTP server
Migrate the access rules for files in the HTTPS media server instance
to the newer 2.4 syntax, matching the HTTP media server in httpd.conf
2025-08-08 10:31:26 +00:00
27af056dce Fix a few ShellCheck reported warnings from PR #213
The checks on the upstream project have reported some warnings to the
code accepted in PR #213, fix them in this commit.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-07 20:20:09 +00:00
e233adfec2 Enable PreferDualStack on all the Services in the subcharts
Make sure that the services are created with both IPv4 and IPv6
addresses when the cluster has been created with both IPv4 and IPv6
ranges. They will behave as single stack otherwise.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:47:00 +00:00
8617c36789 Update the URL for the BMO to connect to Ironic
The BMO should now connect via the provisioningHostname if set or an IP
address. Add a helper that returns the ironic hostname or correctly
formatted IP to define the ironicApiHost variable in the BMO configmap.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:47:00 +00:00
aa56c231d4 Include the hostname for SAN in Certificates
Recently provisioningHostname has been introduced as an alternative way
to configure the IPs to bind and respond to. This however requires that
the Certificates for HTTPS also include a dnsNames section whenver such
value is present.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:47:00 +00:00
29dd8dda17 Introduce metal3.provisioningIP template and deprecate ironicIP
So far ironicIP has been part of values.yaml under the global section,
however this is very misleading: this variable is internal to the Ironic
startup scripts and should not be set, moreover it conflicts with
provisioningIP, which is instead a public configuration variable for the
purpose.

This commits thus introduces the following changes:
- removes the creation of IRONIC_IP in the Ironic configmap
- does not yet remove ironicIP from values.yaml to avoid breaking
  forward compatibility
- introduces a utility function to perform input validation while still
  prioritizing ironicIP if present

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:47:00 +00:00
6012f480b0 Allow to change the LISTEN_ALL_INTERFACE variable for Ironic
It should be possible to enable or disable the environment variable
LISTEN_ALL_INTERFACE in the Ironic configmap, as it allows to the way
Ironic binds to socket, especially in combination with the changes
introduced in v29.

However, if listenOnAll is false, Ironic will bind to a specific IPv4
and/or IPv6 address and the 127.0.0.1 address used for the liveness
and readiness probe will not be accepted. Also add a named template
that, when it is set to false, picks a different host IP or address,
according to the following priority:
- ironicIP (deprecated)
- provisioningIP
- provisioningHostname

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:47:00 +00:00
110a7b1f7c Introduce the provisioningHostname env variable in Ironic
Create a new provisioningHostname value in values.yaml in order to set
the new IRONIC_URL_HOSTNAME, that allows to set the address(es) Ironic
will bind to.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:30:27 +00:00
343fcd24b7 Remove unused env and helm variables
Since currently we can only define the provisioning network and the
external HTTP host, remove some clutter generating unused variables.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:30:26 +00:00
03d7a39ead Allow control over IRONIC_EXTERNAL_HTTP_URL via values.yaml
The purpose of this commit is to:
- avoid providing IRONIC_EXTERNAL_HTTP_URL by default, as the Ironic
  startup scripts will be able to derive the value from other variables
- define a new global value under the top values.yaml to generate
  IRONIC_EXTERNAL_HTTP_URL when actually needed
- make sure that the input, which can either be a hostname or an IP
  address, is correctly formatted in case of an IPv6.

This change also allows subsequent cleanups of the whole Configmap
template for Ironic.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-06 17:30:26 +00:00
e2d38a867c Let Apache use separate IPv4 and IPv6 sockets for listening to any
Enable the use of two separate sockets for IPv4 and IPv6 when
LISTEN_ALL_INTERFACES is set to true. While desirable, on Linux Apache uses
IPv4-mapped IPv6 addresses by default, thus leveraging a single IPv6 socket
for IPv4 connections as well.

This behaviour is far from being desirable and can be disabled at compile
time via the "--disable-v4-mapped" flag, so make sure both an ANY address
Listen directive is present for both IPv4 and IPv6. When Apache is compiled
with "--enable-v4-mapped", the IPv4 socket will be simply ignored.

Please see https://httpd.apache.org/docs/2.4/bind.html for more
information.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
eecd30e90d Update httpd.conf to bind to IPv4 and/or IPv6 sockets
Enable the use of individual IPv4 and IPv6 sockets when the respective
IP is detected and LISTEN_ALL_INTERFACES is not set to true. This allows
to correctly bind to both the IPv4 and IPv6 addresses found and not just
one of them.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
fc0cfda2c0 Let Ironic API use IPv4 and IPv6 sockets when possible
When LISTEN_ALL_INTERFACES is not set, Apache should make Ironic API
avaiable on either or both IPv4 and IPv6 sockets, depending on the
addresses requested or found on the system.

Make sure to set the "Listen" directive according to ENABLE_IPV4 and
ENABLE_IPV4, and the VirtualHost when IRONIC_URL_HOSTNAME is present.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
582aaaa424 Set host_ip to an IPv6 address when found
Prioritize IPv6 over IPv4 when available to set host_ip in ironic.conf
when LISTEN_ALL_INTERFACES is not set to true.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
a94cde2a35 Use my_ipv6 when IRONIC_IPV6 is defined in ironic.conf
As per the Ironic documentation:

"This field [my_ip] does accept an IPv6 address as an override for templates
and URLs, however it is recommended that [DEFAULT]my_ipv6 is used along with
DNS names for service URLs for dual-stack environments."

Fill my_ipv6 when an IPv6 address has been found for binding.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
ad01fecc4f Allow binding on the provisioning network via a hostname
In a dual-stack scenario, especially when deploying in direct mode via
virtual media, it might be useful to 1) use a hostname to enable "dual IP"
URLs 2) have ironic bind to those two addresses, if found on the system.

To make this possible, this commit introduces:
- a new user environment variable named IRONIC_URL_HOSTNAME, to be used
  as immutable external only input, to derive IRONIC_URL_HOST and the
  IP addresses to bind on
- a new utility function named "get_ip_of_hostname" to help look up the
  A and AAAA records
- additional logic to look for the returned address on the system, for
  binding the processes; this new logic has lower priority than
  PROVISIONING_IP (which can then be used to enforce one specific IP
  version) and PROVISIONING_INTERFACE

Note, while IRONIC_URL_HOSTNAME and PROVISIONING_IP are considered to be
mutually exclusive, IRONIC_URL_HOSTNAME and PROVISIONING_INTERFACE are
not.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
d59126b517 Introduce IRONIC_IPV6 to bind on IPv6 sockets
The ironic scripts either use PROVISIONING_IP as an input or try to
determine an IP address to bind the sockets to. This results in
IRONIC_IP being defined once the process is complete, and it can carry
either an IPv4 or an IPv6 address.

Likely, the assumption is that on Linux, by default, IPv4-mapped IPv6
addresses can be leveraged to serve both IPv4 and IPv6 through a single
socket. However this is not a good practice and two separate sockets
should be used instead, whenever possible.

This change modifies such logic by
- introducing the variable IRONIC_IPV6 alongside the existing
- matching IRONIC_IP and attempting to populate both variables

Please note that hostname based URLs, with both A and AAAA records, are
also required for a fully working dual-stack configuration.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
19394a8b03 Revert 2742439 being now redundant
Commit 2742439 added logic to tentatively identify the interface name
in get_provisioning_interface if the PROVISIONING_IP is provided.
However the same process in then repeated in wait_for_interface_or_ip.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
ca7da400d0 Leverage get_interface_of_ip to look PROVISIONING_IP up
Use the previously introduced get_interface_of_ip, to determine if the
PROVISIONING_IP address is actually present on a network interface.

This improves the code readability and enables additional debugging
output.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
c69044ff2b Add two new utility functions for later refactoring
The way the ironic-image processes are bound to internet sockets is mainly
by PROVISIONING_IP or PROVISIONING_INTERFACE, that is, by looking up a
specific address on an interface, or a specific interface for a workable
address.

Introduce two new utility functions in ironic-common.sh for these two
purposes:
get_interface_of_ip: returns the name of the interface where the IP address
                     provided as argument is found
get_ip_of_interface: returns the first IP associated to the interface
                     provided as argument

These two functions will be put into use in subsequent commits.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
60f0bdd5f0 Remove PROVISIONING_INTERFACE default for better validation
Whenever PROVISIONING_INTERFACE is not set by the user, function
get_provisioning_interface attempts to determine one, or provide
"provisionign" as default value. However this can cause confusing errors
down the line.

Remove this default value and fail gracefully, with proper logging,
if the PROVISIONING_INTERFACE value is not detected.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
4e4f9e591a Simplify the setting of host_ip in ironic.conf
The value of host_ip is determined twice within the ironic.conf.j2 template
file, by means of a relatively hard to read set of conditions.

Avoid this duplication and improve readability by exporting the correct
value once in scripts/configure-ironic.sh. This also leave more room for
more complex evaluations should these be needed in the future.

Signed-off-by: Marco Chiappero <marco.chiappero@suse.com>
2025-08-04 14:47:42 +00:00
22 changed files with 344 additions and 72 deletions

View File

@@ -19,11 +19,11 @@ RUN sed -i -e 's%^# rpm.install.excludedocs = no.*%rpm.install.excludedocs = yes
#!ArchExclusiveLine: x86_64
RUN if [ "$(uname -m)" = "x86_64" ];then \
zypper --installroot /installroot --non-interactive install --no-recommends syslinux python311-devel python311 python311-pip python311-sushy-oem-idrac python311-proliantutils python311-sushy python311-pyinotify python3-ironicclient git curl sles-release tar gzip vim gawk dnsmasq dosfstools apache2 apache2-mod_wsgi ipcalc ipmitool iproute2 procps qemu-tools sqlite3 util-linux xorriso tftp ipxe-bootimgs python311-sushy-tools crudini openstack-ironic; \
zypper --installroot /installroot --non-interactive install --no-recommends syslinux python311-devel python311 python311-pip python311-sushy-oem-idrac python311-proliantutils python311-sushy python311-pyinotify python3-ironicclient git curl sles-release tar gzip vim gawk dnsmasq dosfstools apache2 apache2-mod_wsgi ipcalc ipmitool iproute2 bind-utils procps qemu-tools sqlite3 util-linux xorriso tftp ipxe-bootimgs python311-sushy-tools crudini openstack-ironic; \
fi
#!ArchExclusiveLine: aarch64
RUN if [ "$(uname -m)" = "aarch64" ];then \
zypper --installroot /installroot --non-interactive install --no-recommends python311-devel python311 python311-pip python311-sushy-oem-idrac python311-proliantutils python311-sushy python311-pyinotify python3-ironicclient git curl sles-release tar gzip vim gawk dnsmasq dosfstools apache2 apache2-mod_wsgi ipcalc ipmitool iproute2 procps qemu-tools sqlite3 util-linux xorriso tftp ipxe-bootimgs python311-sushy-tools crudini openstack-ironic; \
zypper --installroot /installroot --non-interactive install --no-recommends python311-devel python311 python311-pip python311-sushy-oem-idrac python311-proliantutils python311-sushy python311-pyinotify python3-ironicclient git curl sles-release tar gzip vim gawk dnsmasq dosfstools apache2 apache2-mod_wsgi ipcalc ipmitool iproute2 bind-utils procps qemu-tools sqlite3 util-linux xorriso tftp ipxe-bootimgs python311-sushy-tools crudini openstack-ironic; \
fi
# DATABASE

View File

@@ -1,4 +1,5 @@
Listen {{ env.IPXE_TLS_PORT }}
Listen 0.0.0.0:{{ env.IPXE_TLS_PORT }}
Listen [::]:{{ env.IPXE_TLS_PORT }}
<VirtualHost *:{{ env.IPXE_TLS_PORT }}>
ErrorLog /dev/stderr

View File

@@ -1,4 +1,5 @@
Listen {{ env.VMEDIA_TLS_PORT }}
Listen 0.0.0.0:{{ env.VMEDIA_TLS_PORT }}
Listen [::]:{{ env.VMEDIA_TLS_PORT }}
<VirtualHost *:{{ env.VMEDIA_TLS_PORT }}>
ErrorLog /dev/stderr
@@ -10,13 +11,11 @@ Listen {{ env.VMEDIA_TLS_PORT }}
SSLCertificateFile {{ env.IRONIC_VMEDIA_CERT_FILE }}
SSLCertificateKeyFile {{ env.IRONIC_VMEDIA_KEY_FILE }}
<Directory ~ "/shared/html">
Order deny,allow
deny from all
<Directory "/shared/html/">
Require all granted
</Directory>
<Directory ~ "/shared/html/(redfish|ilo)/">
Order allow,deny
allow from all
Require all granted
</Directory>
</VirtualHost>

View File

@@ -12,11 +12,21 @@
{% if env.LISTEN_ALL_INTERFACES | lower == "true" %}
Listen {{ env.IRONIC_LISTEN_PORT }}
Listen 0.0.0.0:{{ env.IRONIC_LISTEN_PORT }}
Listen [::]:{{ env.IRONIC_LISTEN_PORT }}
<VirtualHost *:{{ env.IRONIC_LISTEN_PORT }}>
{% else %}
Listen {{ env.IRONIC_URL_HOST }}:{{ env.IRONIC_LISTEN_PORT }}
<VirtualHost {{ env.IRONIC_URL_HOST }}:{{ env.IRONIC_LISTEN_PORT }}>
{% if env.ENABLE_IPV4 %}
Listen {{ env.IRONIC_IP }}:{{ env.IRONIC_LISTEN_PORT }}
{% endif %}
{% if env.ENABLE_IPV6 %}
Listen [{{ env.IRONIC_IPV6 }}]:{{ env.IRONIC_LISTEN_PORT }}
{% endif %}
{% if env.IRONIC_URL_HOSTNAME is defined and env.IRONIC_URL_HOSTNAME|length %}
<VirtualHost {{ env.IRONIC_URL_HOSTNAME }}:{{ env.IRONIC_LISTEN_PORT }}>
{% else %}
<VirtualHost {% if env.ENABLE_IPV4 %}{{ env.IRONIC_IP }}:{{ env.IRONIC_LISTEN_PORT }}{% endif %} {% if env.ENABLE_IPV6 %}[{{ env.IRONIC_IPV6 }}]:{{ env.IRONIC_LISTEN_PORT }}{% endif %}>
{% endif %}
{% endif %}
{% if env.IRONIC_PRIVATE_PORT == "unix" %}

View File

@@ -1,8 +1,14 @@
ServerRoot {{ env.HTTPD_DIR }}
{%- if env.LISTEN_ALL_INTERFACES | lower == "true" %}
Listen {{ env.HTTP_PORT }}
Listen 0.0.0.0:{{ env.HTTP_PORT }}
Listen [::]:{{ env.HTTP_PORT }}
{% else %}
Listen {{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }}
{% if env.ENABLE_IPV4 %}
Listen {{ env.IRONIC_IP }}:{{ env.HTTP_PORT }}
{% endif %}
{% if env.ENABLE_IPV6 %}
Listen [{{ env.IRONIC_IPV6 }}]:{{ env.HTTP_PORT }}
{% endif %}
{% endif %}
Include /etc/httpd/conf.modules.d/*.conf
User ironic-suse

View File

@@ -25,7 +25,13 @@ rpc_transport = none
use_stderr = true
# NOTE(dtantsur): the default md5 is not compatible with FIPS mode
hash_ring_algorithm = sha256
{% if env.ENABLE_IPV4 %}
my_ip = {{ env.IRONIC_IP }}
{% endif %}
{% if env.ENABLE_IPV6 %}
my_ipv6 = {{ env.IRONIC_IPV6 }}
{% endif %}
host = {{ env.IRONIC_CONDUCTOR_HOST }}
tempdir = {{ env.IRONIC_TMP_DATA_DIR }}
@@ -65,7 +71,7 @@ port = {{ env.IRONIC_PRIVATE_PORT }}
{% endif %}
public_endpoint = {{ env.IRONIC_BASE_URL }}
{% else %}
host_ip = {% if env.LISTEN_ALL_INTERFACES | lower == "true" %}::{% else %}{{ env.IRONIC_IP }}{% endif %}
host_ip = {{ env.IRONIC_HOST_IP }}
port = {{ env.IRONIC_LISTEN_PORT }}
{% if env.IRONIC_TLS_SETUP == "true" %}
enable_ssl_api = true
@@ -181,7 +187,7 @@ cipher_suite_versions = 3,17
# containers are in host networking.
auth_strategy = http_basic
http_basic_auth_user_file = {{ env.IRONIC_RPC_HTPASSWD_FILE }}
host_ip = {% if env.LISTEN_ALL_INTERFACES | lower == "true" %}::{% else %}{{ env.IRONIC_IP }}{% endif %}
host_ip = {{ env.IRONIC_HOST_IP }}
{% if env.IRONIC_TLS_SETUP == "true" %}
use_ssl = true
cafile = {{ env.IRONIC_CACERT_FILE }}

View File

@@ -51,6 +51,14 @@ export IRONIC_IPA_COLLECTORS=${IRONIC_IPA_COLLECTORS:-default,logs}
wait_for_interface_or_ip
if [[ "$(echo "$LISTEN_ALL_INTERFACES" | tr '[:upper:]' '[:lower:]')" == "true" ]]; then
export IRONIC_HOST_IP="::"
elif [[ -n "${ENABLE_IPV6}" ]]; then
export IRONIC_HOST_IP="$IRONIC_IPV6"
else
export IRONIC_HOST_IP="$IRONIC_IP"
fi
# Hostname to use for the current conductor instance.
export IRONIC_CONDUCTOR_HOST=${IRONIC_CONDUCTOR_HOST:-${IRONIC_URL_HOST}}
@@ -92,4 +100,11 @@ render_j2_config "/etc/ironic/ironic.conf.j2" \
configure_json_rpc_auth
# Make sure ironic traffic bypasses any proxies
export NO_PROXY="${NO_PROXY:-},$IRONIC_IP"
export NO_PROXY="${NO_PROXY:-}"
if [[ -n "$IRONIC_IPV6" ]]; then
export NO_PROXY="${NO_PROXY},${IRONIC_IPV6}"
fi
if [[ -n "$IRONIC_IP" ]]; then
export NO_PROXY="${NO_PROXY},${IRONIC_IP}"
fi

View File

@@ -5,9 +5,11 @@ set -euxo pipefail
# Export IRONIC_IP to avoid needing to lean on IRONIC_URL_HOST for consumption in
# e.g. dnsmasq configuration
export IRONIC_IP="${IRONIC_IP:-}"
export IRONIC_IPV6="${IRONIC_IPV6:-}"
PROVISIONING_INTERFACE="${PROVISIONING_INTERFACE:-}"
PROVISIONING_IP="${PROVISIONING_IP:-}"
PROVISIONING_MACS="${PROVISIONING_MACS:-}"
IRONIC_URL_HOSTNAME="${IRONIC_URL_HOSTNAME:-}"
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}"
@@ -33,6 +35,85 @@ export LOCAL_DB_URI="sqlite:///${IRONIC_DB_DIR}/ironic.sqlite"
export IRONIC_USE_MARIADB=${IRONIC_USE_MARIADB:-false}
get_ip_of_hostname()
{
if [[ "$#" -ne 2 ]]; then
echo "${FUNCNAME}: two parameters required, $# provided" >&2
return 1
fi
case $2 in
4)
QUERY="a";;
6)
QUERY="aaaa";;
*)
echo "${FUNCNAME}: the second parameter should be [a|aaaa] for A and AAAA records"
return 1;;
esac
local HOSTNAME=$1
echo $(nslookup -type=${QUERY} "${HOSTNAME}" | tail -n2 | grep -w "Address:" | cut -d " " -f2)
}
get_interface_of_ip()
{
local IP_VERS=""
if [[ "$#" -gt 2 ]]; then
echo "${FUNCNAME}: too many parameters" >&2
return 1
fi
if [[ "$#" -eq 2 ]]; then
case $2 in
4|6)
local IP_VERS="-${2}"
;;
*)
echo "${FUNCNAME}: the second parameter should be [4|6] (or missing for both)" >&2
return 2
;;
esac
fi
local IP_ADDR=$1
# Convert the address using ipcalc which strips out the subnet.
# For IPv6 addresses, this will give the short-form address
IP_ADDR="$(ipcalc "${IP_ADDR}" | grep "^Address:" | awk '{print $2}')"
echo $(ip ${IP_VERS} -br addr show scope global | grep -i " ${IP_ADDR}/" | cut -f 1 -d ' ' | cut -f 1 -d '@')
}
get_ip_of_interface()
{
local IP_VERS=""
if [[ "$#" -gt 2 ]]; then
echo "${FUNCNAME}: too many parameters" >&2
return 1
fi
if [[ "$#" -eq 2 ]]; then
case $2 in
4|6)
local IP_VERS="-${2}"
;;
*)
echo "${FUNCNAME}: the second parameter should be [4|6] (or missing for both)" >&2
return 2
;;
esac
fi
local IFACE=$1
echo $(ip ${IP_VERS} -br addr show scope global up dev ${IFACE} | awk '{print $3}' | sed -e 's%/.*%%' | head -n 1)
}
get_provisioning_interface()
{
if [[ -n "$PROVISIONING_INTERFACE" ]]; then
@@ -41,13 +122,7 @@ get_provisioning_interface()
return
fi
local interface="provisioning"
if [[ -n "${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
local interface=""
for mac in ${PROVISIONING_MACS//,/ }; do
if ip -br link show up | grep -i "$mac" &>/dev/null; then
@@ -71,32 +146,105 @@ wait_for_interface_or_ip()
# 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
echo "Waiting for ${IRONIC_IP} to be configured on an interface"
local IFACE_OF_IP=""
until [[ -n "$IFACE_OF_IP" ]]; do
echo "Waiting for ${PROVISIONING_IP} to be configured on an interface..."
IFACE_OF_IP="$(get_interface_of_ip "${PROVISIONING_IP}")"
sleep 1
done
echo "Found $PROVISIONING_IP on interface \"${IFACE_OF_IP}\"!"
export PROVISIONING_INTERFACE="$IFACE_OF_IP"
# If the IP contains a colon, then it's an IPv6 address
if [[ "$PROVISIONING_IP" =~ .*:.* ]]; then
export IRONIC_IPV6="$PROVISIONING_IP"
else
export IRONIC_IP="$PROVISIONING_IP"
fi
elif [[ -n "${PROVISIONING_INTERFACE}" ]]; then
until [[ -n "$IRONIC_IPV6" ]] || [[ -n "$IRONIC_IP" ]]; do
echo "Waiting for ${PROVISIONING_INTERFACE} interface to be configured..."
IRONIC_IPV6="$(get_ip_of_interface "${PROVISIONING_INTERFACE}" 6)"
sleep 1
IRONIC_IP="$(get_ip_of_interface "${PROVISIONING_INTERFACE}" 4)"
sleep 1
done
if [[ -n "$IRONIC_IPV6" ]]; then
echo "Found $IRONIC_IPV6 on interface \"${PROVISIONING_INTERFACE}\"!"
export IRONIC_IPV6
fi
if [[ -n "$IRONIC_IP" ]]; then
echo "Found $IRONIC_IP on interface \"${PROVISIONING_INTERFACE}\"!"
export IRONIC_IP
fi
elif [[ -n "$IRONIC_URL_HOSTNAME" ]]; then
local IPV6_IFACE=""
local IPV4_IFACE=""
# we should get at least one IP address
until [[ -n "$IPV6_IFACE" ]] || [[ -n "$IPV4_IFACE" ]]; do
local IPV6_RECORD=""
local IPV4_RECORD=""
IPV6_RECORD="$(get_ip_of_hostname "${IRONIC_URL_HOSTNAME}" 6)"
IPV4_RECORD="$(get_ip_of_hostname "${IRONIC_URL_HOSTNAME}" 4)"
# We couldn't get any IP
if [[ -z "$IPV4_RECORD" ]] && [[ -z "$IPV6_RECORD" ]]; then
echo "${FUNCNAME}: no valid IP found for hostname ${IRONIC_URL_HOSTNAME}" >&2
return 1
fi
echo "Waiting for ${IPV6_RECORD} to be configured on an interface"
IPV6_IFACE="$(get_interface_of_ip "${IPV6_RECORD}" 6)"
sleep 1
echo "Waiting for ${IPV4_RECORD} to be configured on an interface"
IPV4_IFACE="$(get_interface_of_ip "${IPV4_RECORD}" 4)"
sleep 1
done
# Add some debugging output
if [[ -n "$IPV6_IFACE" ]]; then
echo "Found $IPV6_RECORD on interface \"${IPV6_IFACE}\"!"
export IRONIC_IPV6="$IPV6_RECORD"
fi
if [[ -n "$IPV4_IFACE" ]]; then
echo "Found $IPV4_RECORD on interface \"${IPV4_IFACE}\"!"
export IRONIC_IP="$IPV4_RECORD"
fi
# Make sure both IPs are asigned to the same interface
if [[ -n "$IPV6_IFACE" ]] && [[ -n "$IPV4_IFACE" ]] && [[ "$IPV6_IFACE" != "$IPV4_IFACE" ]]; then
echo "Warning, the IPv4 and IPv6 addresses from \"${HOSTNAME}\" are assigned to different " \
"interfaces (\"${IPV6_IFACE}\" and \"${IPV4_IFACE}\")" >&2
fi
else
until [[ -n "$IRONIC_IP" ]]; do
echo "Waiting for ${PROVISIONING_INTERFACE} interface to be configured"
IRONIC_IP="$(ip -br add show scope global up dev "${PROVISIONING_INTERFACE}" | awk '{print $3}' | sed -e 's%/.*%%' | head -n 1)"
export IRONIC_IP
sleep 1
done
echo "Cannot determine an interface or an IP for binding and creating URLs"
return 1
fi
# If the IP contains a colon, then it's an IPv6 address, and the HTTP
# host needs surrounding with brackets
if [[ "$IRONIC_IP" =~ .*:.* ]]; then
export IPV=6
export IRONIC_URL_HOST="[$IRONIC_IP]"
else
export IPV=4
# Define the URLs based on the what we have found,
# prioritize IPv6 for IRONIC_URL_HOST
if [[ -n "$IRONIC_IP" ]]; then
export ENABLE_IPV4=yes
export IRONIC_URL_HOST="$IRONIC_IP"
fi
if [[ -n "$IRONIC_IPV6" ]]; then
export ENABLE_IPV6=yes
export IRONIC_URL_HOST="[${IRONIC_IPV6}]" # The HTTP host needs surrounding with brackets
fi
# Once determined if we have IPv4 and/or IPv6, override the hostname if provided
if [[ -n "$IRONIC_URL_HOSTNAME" ]]; then
IRONIC_URL_HOST=$IRONIC_URL_HOSTNAME
fi
# Avoid having to construct full URL multiple times while allowing
# the override of IRONIC_HTTP_URL for environments in which IRONIC_IP

View File

@@ -61,3 +61,19 @@ Create the name of the service account to use
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create the URL to use for connecting to the Ironic servers (e.g. API, cache)
*/}}
{{- define "baremetal-operator.ironicHttpHost" -}}
{{- $ironicIP := include "metal3.provisioningIP" . -}}
{{- with .Values.global }}
{{- if .provisioningHostname }}
{{- .provisioningHostname }}
{{- else if regexMatch ".*:.*" $ironicIP}}
{{- print "[" $ironicIP "]" }}
{{- else }}
{{- $ironicIP }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -1,10 +1,10 @@
{{- $enableTLS := .Values.global.enable_tls }}
{{- $enableVMediaTLS := .Values.global.enable_vmedia_tls }}
{{- $protocol := ternary "https" "http" $enableTLS }}
{{- $ironicIP := .Values.global.ironicIP | default "" }}
{{- $ironicApiHost := print $ironicIP ":6385" }}
{{- $ironicBootHost := print $ironicIP ":6180" }}
{{- $ironicCacheHost := print $ironicIP ":6180" }}
{{- $ironicHost := include "baremetal-operator.ironicHttpHost" . | required "Missing host information for BMO to connect to Ironic" }}
{{- $ironicApiHost := print $ironicHost ":6385" }}
{{- $ironicBootHost := print $ironicHost ":6180" }}
{{- $ironicCacheHost := print $ironicHost ":6180" }}
{{- $deployArch := .Values.global.deployArchitecture }}
apiVersion: v1
@@ -12,8 +12,8 @@ data:
IRONIC_ENDPOINT: "{{ $protocol }}://{{ $ironicApiHost }}/v1/"
# Switch VMedia to HTTP if enable_vmedia_tls is false
{{- if and $enableTLS $enableVMediaTLS }}
{{- $ironicBootHost = print $ironicIP ":" .Values.global.vmediaTLSPort }}
{{- $ironicCacheHost = print $ironicIP ":" .Values.global.vmediaTLSPort }}
{{- $ironicBootHost = print $ironicHost ":" .Values.global.vmediaTLSPort }}
{{- $ironicCacheHost = print $ironicHost ":" .Values.global.vmediaTLSPort }}
{{- $protocol = "https" }}
RESTART_CONTAINER_CERTIFICATE_UPDATED: "true"
{{- else }}

View File

@@ -6,6 +6,7 @@ metadata:
control-plane: controller-manager
name: {{ include "baremetal-operator.fullname" . }}-controller-manager-metrics-service
spec:
ipFamilyPolicy: PreferDualStack
ports:
- name: https
port: 8443

View File

@@ -5,6 +5,7 @@ metadata:
{{- include "baremetal-operator.labels" . | nindent 4 }}
name: {{ include "baremetal-operator.fullname" . }}-webhook-service
spec:
ipFamilyPolicy: PreferDualStack
ports:
- port: 443
targetPort: 9443

View File

@@ -83,3 +83,46 @@ Get ironic CA volumeMounts
readOnly: true
{{- end }}
{{- end }}
{{/*
Get the formatted "External" hostname or IP address
*/}}
{{- define "ironic.externalHttpHost" }}
{{- with .Values.global }}
{{- if regexMatch ".*:.*" .externalHttpHost }}
{{- print "[" .externalHttpHost "]" }}
{{- else }}
{{- .externalHttpHost }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Get the command to use for Liveness and Readiness probes
*/}}
{{- define "ironic.probeCommand" }}
{{- $host := "127.0.0.1" }}
{{- if eq .Values.listenOnAll false }}
{{- $host = coalesce .Values.global.ironicIP .Values.global.provisioningIP .Values.global.provisioningHostname }}
{{- if regexMatch ".*:.*" $host }}
{{- $host = print "[" $host "]" }}
{{- end }}
{{- end }}
{{- print "curl -sSfk https://" $host ":6385" }}
{{- end }}
{{/*
Create the subjectAltNames section to be set on the Certificate
*/}}
{{- define "ironic.subjectAltNames" -}}
{{- with .Values.global }}
{{- if .provisioningHostname }}
dnsNames:
- {{ .provisioningHostname }}
{{- end -}}
{{- if or .ironicIP .provisioningIP }}
ipAddresses:
- {{ coalesce .ironicIP .provisioningIP }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -6,8 +6,7 @@ metadata:
spec:
commonName: ironic-ca
isCA: true
ipAddresses:
- {{ .Values.global.ironicIP }}
{{- include "ironic.subjectAltNames" . | indent 2 }}
issuerRef:
kind: Issuer
name: selfsigned-issuer
@@ -19,8 +18,7 @@ metadata:
name: ironic-cert
spec:
commonName: ironic-cert
ipAddresses:
- {{ .Values.global.ironicIP }}
{{- include "ironic.subjectAltNames" . | indent 2 }}
issuerRef:
kind: Issuer
name: ca-issuer
@@ -33,8 +31,7 @@ metadata:
name: ironic-vmedia-cert
spec:
commonName: ironic-vmedia-cert
ipAddresses:
- {{ .Values.global.ironicIP }}
{{- include "ironic.subjectAltNames" . | indent 2 }}
issuerRef:
kind: Issuer
name: ca-issuer

View File

@@ -8,13 +8,9 @@ data:
{{- $enableTLS := .Values.global.enable_tls }}
{{- $enableVMediaTLS := .Values.global.enable_vmedia_tls }}
{{- $protocol := ternary "https" "http" $enableTLS }}
{{- $ironicIP := .Values.global.ironicIP | default "" }}
{{- $ironicBootHost := print $ironicIP ":6180" }}
{{- $ironicCacheHost := print $ironicIP ":6180" }}
{{- $deployArch := .Values.global.deployArchitecture }}
{{- if ( .Values.global.enable_dnsmasq ) }}
DNSMASQ_BOOT_SERVER_ADDRESS: {{ $ironicBootHost }}
DNSMASQ_DNS_SERVER_ADDRESS: {{ .Values.global.dnsmasqDNSServer }}
DNSMASQ_DEFAULT_ROUTER: {{ .Values.global.dnsmasqDefaultRouter }}
DHCP_RANGE: {{ .Values.global.dhcpRange }}
@@ -26,27 +22,25 @@ data:
PREDICTABLE_NIC_NAMES: "{{ .Values.global.predictableNicNames }}"
# Switch VMedia to HTTP if enable_vmedia_tls is false
{{- if and $enableTLS $enableVMediaTLS }}
{{- $ironicBootHost = print $ironicIP ":" .Values.global.vmediaTLSPort }}
{{- $ironicCacheHost = print $ironicIP ":" .Values.global.vmediaTLSPort }}
{{- $protocol = "https" }}
{{- else }}
{{- $protocol = "http" }}
{{- end }}
IRONIC_EXTERNAL_HTTP_URL: {{ $protocol }}://{{ $ironicCacheHost }}
{{- if .Values.global.externalHttpHost }}
IRONIC_EXTERNAL_HTTP_URL: {{ $protocol }}://{{ include "ironic.externalHttpHost" . }}:6385
{{- end }}
DEPLOY_ARCHITECTURE: {{ $deployArch }}
IRONIC_BOOT_BASE_URL: {{ $protocol }}://{{ $ironicBootHost }}
ENABLE_PXE_BOOT: "{{ .Values.global.enable_pxe_boot }}"
{{- if .Values.global.provisioningInterface }}
PROVISIONING_INTERFACE: {{ .Values.global.provisioningInterface }}
{{- end }}
{{- if .Values.global.provisioningIP }}
PROVISIONING_IP: {{ .Values.global.provisioningIP }}
{{- if or .Values.global.ironicIP .Values.global.provisioningIP }}
PROVISIONING_IP: {{ include "metal3.provisioningIP" . }}
{{- else if .Values.global.provisioningHostname }}
IRONIC_URL_HOSTNAME: {{ .Values.global.provisioningHostname }}
{{- end }}
IRONIC_FAST_TRACK: "true"
LISTEN_ALL_INTERFACES: "true"
{{- if .Values.global.ironicIP }}
IRONIC_IP: {{ .Values.global.ironicIP }}
{{- end }}
LISTEN_ALL_INTERFACES: "{{ .Values.listenOnAll }}"
{{- if ( .Values.global.enable_tls ) }}
RESTART_CONTAINER_CERTIFICATE_UPDATED: "true"
IRONIC_KERNEL_PARAMS: {{ .Values.global.ironicKernelParams }} tls.enabled=true

View File

@@ -42,7 +42,7 @@ spec:
name: ironic
livenessProbe:
exec:
command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"]
command: ["sh", "-c", "{{ include "ironic.probeCommand" . }}"]
failureThreshold: 10
initialDelaySeconds: 30
periodSeconds: 30
@@ -60,7 +60,7 @@ spec:
{{- end }}
readinessProbe:
exec:
command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"]
command: ["sh", "-c", "{{ include "ironic.probeCommand" . }}"]
failureThreshold: 10
initialDelaySeconds: 30
periodSeconds: 30

View File

@@ -10,6 +10,7 @@ metadata:
{{- end }}
spec:
type: {{ .Values.service.type }}
ipFamilyPolicy: PreferDualStack
ports:
{{- $enableTLS := .Values.global.enable_tls }}
{{- $enableVMediaTLS := .Values.global.enable_vmedia_tls }}

View File

@@ -32,6 +32,12 @@ global:
# IP Address assigned to network interface on provisioning network
provisioningIP: ""
# Fully Qualified Domain Name used by Ironic for both binding (to the
# associated IPv4 and/or IPv6 addresses) and exposing the API, dnsmask and
# media, also used by BMO. Note, this is the only way to enable a fully
# working dual-stack configuration.
provisioningHostname: ""
# Whether the NIC names should be predictable or not
predictableNicNames: "true"
@@ -52,6 +58,8 @@ global:
replicaCount: 1
listenOnAll: true
images:
ironic:
repository: registry.opensuse.org/isv/suse/edge/metal3/containers/images/ironic

View File

@@ -5,10 +5,11 @@ metadata:
labels:
{{- include "mariadb.labels" . | nindent 4 }}
spec:
ipFamilyPolicy: PreferDualStack
type: {{ .Values.service.type }}
selector:
{{- include "mariadb.selectorLabels" . | nindent 4 }}
ports:
{{- with .Values.service.ports }}
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}

View File

@@ -5,6 +5,7 @@ metadata:
labels:
{{- include "media.labels" . | nindent 4 }}
spec:
ipFamilyPolicy: PreferDualStack
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}

View File

@@ -60,3 +60,18 @@ Create the name of the service account to use
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Produce the correct IP or hostname for Ironic provisioning
*/}}
{{- define "metal3.provisioningIP" -}}
{{- with .Values.global }}
{{- if and .provisioningHostname (or .provisioningIP .ironicIP) }}
{{ fail "Please provide either provisioningHostname or provisioningIP (note: ironic IP is deprecated)" }}
{{- end }}
{{- if and .provisioningIP .ironicIP }}
{{ fail "Please provide either ironicIP or provisioningIP (note: ironicIP is deprecated)" }}
{{- end }}
{{- coalesce .ironicIP .provisioningIP }}
{{- end }}
{{- end }}

View File

@@ -60,6 +60,15 @@ global:
# IP Address assigned to network interface on provisioning network
provisioningIP: ""
# Fully Qualified Domain Name used by Ironic for both binding (to the
# associated IPv4 and/or IPv6 addresses) and exposing the API, dnsmask and
# media, also used by BMO. Note, this is the only way to enable a fully
# working dual-stack configuration.
provisioningHostname: ""
# Hostname or IP for accessing the Ironic API server from a non-provisioning network
externalHttpHost: ""
# Name for the MariaDB service
databaseServiceName: metal3-mariadb