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>
This commit is contained in:
2025-07-23 12:39:20 +00:00
parent d59126b517
commit ad01fecc4f
2 changed files with 73 additions and 3 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

@@ -9,6 +9,7 @@ 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}"
@@ -35,6 +36,28 @@ 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=""
@@ -151,13 +174,55 @@ wait_for_interface_or_ip()
sleep 1
done
# Add some debugging output
if [[ -n "$IRONIC_IPV6" ]]; then
echo "Found $IRONIC_IPV6 on interface \"${PROVISIONING_INTERFACE}\"!"
fi
if [[ -n "$IRONIC_IP" ]]; then
echo "Found $IRONIC_IP on interface \"${PROVISIONING_INTERFACE}\"!"
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
echo "Cannot determine an interface or an IP for binding and creating URLs"
return 1
@@ -174,6 +239,11 @@ wait_for_interface_or_ip()
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
# is unreachable from hosts being provisioned.