Skip to content

Commit db16fcb

Browse files
committed
[update] Add complete IPv6 support to workload launch scripts
Add comprehensive IPv6 support for RHOSO deployments in the update workload launch infrastructure. This enables workload testing on IPv6-only environments that were previously unsupported. Here IPv6-only means that no IPv4 routing is defined at all. **Note:** This requires the deployment of an `IPv6` public network using `cifmw_os_net_setup_config` definition. The critical parameters are: - the name of the external network must be `public` - the name of its associated network must be `public_subnet` - `public_subnet` must have `ip_version` set to `6` The example below show the correct definition: ``` cifmw_os_net_setup_config: - name: public external: true ... subnets: - name: public_subnet ip_version: 6 ``` **Key Features:** - IPv6 VM deployment using direct public network attachment - IPv6-aware security groups and networking rules - IPv6 nameserver parsing for subnet creation - IPv6-specific cleanup and resource management - Conditional timeout handling for IPv6 DHCP behavior **Technical Changes:** - Skip tenant network creation for IPv6 (use public network directly) - Use instance ports instead of floating IPs for IPv6 - Replace regex nameserver parsing with IPv4/IPv6 compatible awk - Fix workload_launch parameter handling for proper timeout control Limitation: SRIOV validation is not supported in IPv6 only setup. Maintains full backward compatibility with existing IPv4 workflows. Signed-off-by: Sofer Athlan-Guyot <sathlang@redhat.com> Partially-Closes: [OSPCIX-1114](https://issues.redhat.com/browse/OSPCIX-1114)
1 parent d790c49 commit db16fcb

File tree

2 files changed

+175
-69
lines changed

2 files changed

+175
-69
lines changed

roles/update/templates/l3_agent_start_ping.sh.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ fi
2525
# we will make the awk wait until this ping exits as it will hold
2626
# the output pipes. So again &>> instead of >> is necessary.
2727

28+
# Modern ping can handle both IPv4 and IPv6 addresses automatically
2829
ping -D "${VM_IP}" &>> "${PING_LOG}" &

roles/update/templates/workload_launch.sh.j2

Lines changed: 174 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,25 @@ function os_cmd {
4343
function set_vm_ip {
4444
## assign floating ip or external ip
4545
local workload_sriov={{workload_sriov|default(false) | bool | ternary("True", "")}}
46+
4647
if [ -n "${workload_sriov}" ]; then
4748
EXTERNAL_IP=$(os_cmd port show "${SRIOV_PORT}" -f yaml -c fixed_ips | awk '/ip_address/{print $3;exit}')
4849
VM_IP=${EXTERNAL_IP}
50+
elif [ "${IS_IPV6}" = "True" ]; then
51+
# For IPv6, get the direct IP from the instance (no floating IPs)
52+
if [ -n "${IPV6_PREFIX}" ]; then
53+
VM_IP=$(os_cmd server show ${INSTANCE_NAME} -f value -c addresses | grep -oE "${IPV6_PREFIX}[a-f0-9:]+")
54+
else
55+
# Fallback to generic IPv6 pattern if prefix extraction fails
56+
VM_IP=$(os_cmd server show ${INSTANCE_NAME} -f value -c addresses | grep -oE '[0-9a-f:]+:[0-9a-f:]+')
57+
fi
58+
# Get the port ID for cleanup
59+
INSTANCE_PORT_ID=$(os_cmd port list --server ${INSTANCE_NAME} -f value -c ID | head -1)
60+
if [ -z "${VM_IP}" ]; then
61+
echo "Cannot get IPv6 address from instance"
62+
exit 66
63+
fi
64+
echo "Using direct IPv6 address: ${VM_IP}"
4965
else
5066
INSTANCE_FIP=$(os_cmd floating ip create -f value -c floating_ip_address "${EXTERNAL_NET_NAME}")
5167
if [ -z "${INSTANCE_FIP}" ]; then
@@ -146,7 +162,18 @@ function prepare_env {
146162
export INSTANCE_FILE="${HOME}/{{ cifmw_update_artifacts_basedir_suffix }}/vm_info.sh"
147163
export WORKLOAD_FILE="${HOME}/{{ cifmw_update_artifacts_basedir_suffix }}/workload_suffix"
148164
export SSH_KEY_FILE="${HOME}/.ssh/${KEYPAIR_NAME}"
165+
{% set ipv6_subnet = cifmw_os_net_setup_config | selectattr('name', 'equalto', 'public') | map(attribute='subnets') | flatten | selectattr('ip_version', 'defined') | selectattr('ip_version', 'equalto', 6) | first | default(none) %}
166+
export IS_IPV6={{ ipv6_subnet is not none }}
167+
export IPV6_PREFIX='{{ ipv6_subnet.cidr | default('') | regex_replace(':[^:]*$', '') if ipv6_subnet else '' }}'
168+
export INSTANCE_PORT_ID="" # Track port ID for IPv6 cleanup
149169
mkdir -p "${HOME}/{{ cifmw_update_artifacts_basedir_suffix }}"
170+
171+
# Validate SRIOV is not used with IPv6 (untested configuration)
172+
local workload_sriov={{workload_sriov|default(false) | bool | ternary("True", "")}}
173+
if [ "${IS_IPV6}" = "True" ] && [ -n "${workload_sriov}" ]; then
174+
echo "ERROR: SRIOV configuration is not supported with IPv6 deployments (untested)"
175+
exit 1
176+
fi
150177
}
151178

152179
function sanity_check {
@@ -187,20 +214,39 @@ function sanity_teardown {
187214

188215
local timeout_seconds=${1:-180}
189216
local elapsed_seconds=0
217+
190218
{% if workload_sriov|default(false) | bool -%}
191-
openstack port delete "${SRIOV_PORT}"
219+
if [ -n "${SRIOV_PORT}" ]; then
220+
echo "Deleting SRIOV port ${SRIOV_PORT}"
221+
openstack port delete "${SRIOV_PORT}" || echo "Warning: Failed to delete SRIOV port ${SRIOV_PORT}"
222+
fi
192223
{% elif workload_dpdk|default(false) | bool -%}
193-
openstack port delete "${DPDK_PORT}"
224+
if [ -n "${DPDK_PORT}" ]; then
225+
echo "Deleting DPDK port ${DPDK_PORT}"
226+
openstack port delete "${DPDK_PORT}" || echo "Warning: Failed to delete DPDK port ${DPDK_PORT}"
227+
fi
194228
{% else -%}
195-
if [ -n "${INSTANCE_FIP}" ]; then
196-
echo "Remove ${INSTANCE_FIP} from ${INSTANCE_NAME}"
197-
openstack server remove floating ip ${INSTANCE_NAME} ${INSTANCE_FIP}
198-
grep "${INSTANCE_FIP}" "${INSTANCE_FILE}" && rm "${INSTANCE_FILE}"
229+
if [ "${IS_IPV6}" = "True" ]; then
230+
# For IPv6, clean up port if tracked
231+
if [ -n "${INSTANCE_PORT_ID}" ]; then
232+
echo "IPv6 deployment - deleting port ${INSTANCE_PORT_ID}"
233+
openstack port delete "${INSTANCE_PORT_ID}" || echo "Warning: Failed to delete port ${INSTANCE_PORT_ID}"
234+
else
235+
echo "IPv6 deployment - no floating IP to clean up"
236+
fi
237+
else
238+
# For IPv4, clean up floating IP
239+
if [ -n "${INSTANCE_FIP}" ]; then
240+
echo "Remove ${INSTANCE_FIP} from ${INSTANCE_NAME}"
241+
openstack server remove floating ip ${INSTANCE_NAME} ${INSTANCE_FIP} || echo "Warning: Failed to remove floating IP from instance"
242+
if [ -f "${INSTANCE_FILE}" ] && grep -q "${INSTANCE_FIP}" "${INSTANCE_FILE}"; then
243+
rm "${INSTANCE_FILE}"
244+
fi
199245

200-
echo "Delete floating ip ${INSTANCE_FIP}"
201-
openstack floating ip delete ${INSTANCE_FIP}
246+
echo "Delete floating ip ${INSTANCE_FIP}"
247+
openstack floating ip delete ${INSTANCE_FIP} || echo "Warning: Failed to delete floating IP ${INSTANCE_FIP}"
248+
fi
202249
fi
203-
204250
{%- endif %}
205251

206252
{% if cifmw_update_create_volume | default(True) | bool -%}
@@ -248,20 +294,25 @@ function sanity_teardown {
248294
fi
249295
{%- endif %}
250296

251-
echo "Clear default gateway from ${TENANT_NET_NAME}_router"
252-
openstack router unset --external-gateway "${TENANT_NET_NAME}"_router
297+
# Only clean up tenant network resources if they were created (IPv4 mode)
298+
if [ "${IS_IPV6}" != "True" ]; then
299+
echo "Clear default gateway from ${TENANT_NET_NAME}_router"
300+
openstack router unset --external-gateway "${TENANT_NET_NAME}"_router || echo "Warning: Failed to clear gateway"
253301

254-
echo "Remove subnet ${TENANT_NET_NAME}_subnet from router ${TENANT_NET_NAME}_router"
255-
openstack router remove subnet "${TENANT_NET_NAME}"_router "${TENANT_NET_NAME}"_subnet
302+
echo "Remove subnet ${TENANT_NET_NAME}_subnet from router ${TENANT_NET_NAME}_router"
303+
openstack router remove subnet "${TENANT_NET_NAME}"_router "${TENANT_NET_NAME}"_subnet || echo "Warning: Failed to remove subnet from router"
256304

257-
echo "Remove router ${TENANT_NET_NAME}_router"
258-
openstack router delete "${TENANT_NET_NAME}_router"
305+
echo "Remove router ${TENANT_NET_NAME}_router"
306+
openstack router delete "${TENANT_NET_NAME}_router" || echo "Warning: Failed to delete router"
259307

260-
echo "Remove subnet ${TENANT_NET_NAME}_subnet"
261-
openstack subnet delete "${TENANT_NET_NAME}_subnet"
308+
echo "Remove subnet ${TENANT_NET_NAME}_subnet"
309+
openstack subnet delete "${TENANT_NET_NAME}_subnet" || echo "Warning: Failed to delete subnet"
262310

263-
echo "Remove network ${TENANT_NET_NAME}"
264-
openstack network delete "${TENANT_NET_NAME}"
311+
echo "Remove network ${TENANT_NET_NAME}"
312+
openstack network delete "${TENANT_NET_NAME}" || echo "Warning: Failed to delete network"
313+
else
314+
echo "IPv6 deployment - skipping tenant network cleanup (using public network directly, bootstrap router persists)"
315+
fi
265316

266317
echo "Remove security group ${SECGROUP_NAME}"
267318
openstack security group delete "${SECGROUP_NAME}"
@@ -283,7 +334,7 @@ function sanity_teardown {
283334
function workload_launch {
284335
# create workload
285336
timeout_seconds=${1:-120}
286-
ssh_timeout_seconds=${1:-180}
337+
ssh_timeout_seconds=${2:-180}
287338
local workload_sriov={{workload_sriov|default(false) | bool | ternary("True", "")}}
288339
local workload_dpdk={{workload_dpdk|default(false) | bool | ternary("True", "")}}
289340

@@ -338,47 +389,82 @@ function workload_launch {
338389
fi
339390
fi
340391

341-
## create networking
342-
openstack network list | grep ${TENANT_NET_NAME}
343-
if [ $? -ne 0 ]; then
344-
NAMESERVER=$(grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/resolv.conf | head -1)
345-
echo "Creating router ${TENANT_NET_NAME}_router"
346-
os_cmd router create ${TENANT_NET_NAME}_router
392+
## create networking for IPv4
393+
if [ "${IS_IPV6}" != "True" ]; then
394+
openstack network list | grep ${TENANT_NET_NAME}
395+
if [ $? -ne 0 ]; then
396+
# Collect all nameservers from resolv.conf
397+
NAMESERVERS=$(awk '/^nameserver[[:space:]]/ {print $2}' /etc/resolv.conf)
398+
399+
# Build DNS arguments
400+
DNS_ARGS=""
401+
if [ -n "$NAMESERVERS" ]; then
402+
for ns in $NAMESERVERS; do
403+
DNS_ARGS="$DNS_ARGS --dns-nameserver $ns"
404+
done
405+
else
406+
echo "Warning: No nameservers found, using public DNS fallback"
407+
DNS_ARGS="--dns-nameserver 8.8.8.8"
408+
fi
409+
echo "Creating router ${TENANT_NET_NAME}_router"
410+
os_cmd router create ${TENANT_NET_NAME}_router
411+
412+
echo "Creating network ${TENANT_NET_NAME}"
413+
if [ -n "${workload_dpdk}" ]; then
414+
os_cmd network create ${TENANT_NET_NAME} --provider-network-type geneve
415+
else
416+
os_cmd network create ${TENANT_NET_NAME}
417+
fi
347418

348-
echo "Creating network ${TENANT_NET_NAME}"
349-
if [ -n "${workload_dpdk}" ]; then
350-
os_cmd network create ${TENANT_NET_NAME} --provider-network-type geneve
351-
else
352-
os_cmd network create ${TENANT_NET_NAME}
353-
fi
419+
echo "Creating subnet ${TENANT_NET_NAME}_subnet"
420+
os_cmd subnet create \
421+
--subnet-range 192.168.0.0/24 \
422+
--allocation-pool start=192.168.0.10,end=192.168.0.100 \
423+
--gateway 192.168.0.254 \
424+
$DNS_ARGS \
425+
--network ${TENANT_NET_NAME} \
426+
${TENANT_NET_NAME}_subnet
354427

355-
echo "Creating subnet ${TENANT_NET_NAME}_subnet"
356-
os_cmd subnet create \
357-
--subnet-range 192.168.0.0/24 \
358-
--allocation-pool start=192.168.0.10,end=192.168.0.100 \
359-
--gateway 192.168.0.254 \
360-
--dns-nameserver ${NAMESERVER} \
361-
--network ${TENANT_NET_NAME} \
362-
${TENANT_NET_NAME}_subnet
428+
echo "Add subnet ${TENANT_NET_NAME}_subnet to router ${TENANT_NET_NAME}_router"
429+
os_cmd router add subnet ${TENANT_NET_NAME}_router ${TENANT_NET_NAME}_subnet
363430

364-
echo "Add subnet ${TENANT_NET_NAME}_subnet to router ${TENANT_NET_NAME}_router"
365-
os_cmd router add subnet ${TENANT_NET_NAME}_router ${TENANT_NET_NAME}_subnet
431+
echo "Set external-gateway for ${TENANT_NET_NAME}_router"
432+
os_cmd router set --external-gateway ${EXTERNAL_NET_NAME} ${TENANT_NET_NAME}_router
433+
fi
434+
else
435+
# For IPv6, ensure OVN Router Advertisement bootstrap router exists
436+
# This is infrastructure-level and persists across all workload operations
437+
openstack router list | grep "ovn-ipv6-bootstrap-router"
438+
if [ $? -ne 0 ]; then
439+
echo "Creating OVN IPv6 bootstrap router (infrastructure-level, required for SLAAC)"
440+
os_cmd router create ovn-ipv6-bootstrap-router
366441

367-
echo "Set external-gateway for ${TENANT_NET_NAME}_router"
368-
os_cmd router set --external-gateway ${EXTERNAL_NET_NAME} ${TENANT_NET_NAME}_router
442+
echo "Set external-gateway for OVN IPv6 bootstrap router"
443+
os_cmd router set --external-gateway ${EXTERNAL_NET_NAME} ovn-ipv6-bootstrap-router
444+
fi
369445
fi
370-
371446
## create security group
372447
openstack security group list | grep ${SECGROUP_NAME}
373448
if [ $? -ne 0 ]; then
374449
echo "Creating security group ${SECGROUP_NAME}"
375450
os_cmd security group create ${SECGROUP_NAME}
376451

452+
# Set protocol and ethertype based on IP version
453+
if [ "${IS_IPV6}" = "True" ]; then
454+
ICMP_PROTO="ipv6-icmp"
455+
ETHERTYPE_ARG="--ethertype IPv6"
456+
echo "Creating IPv6 rules for ports 22,80,443 in security group ${SECGROUP_NAME}"
457+
else
458+
ICMP_PROTO="icmp"
459+
ETHERTYPE_ARG=""
460+
echo "Creating IPv4 rules for ports 22,80,443 in security group ${SECGROUP_NAME}"
461+
fi
462+
377463
echo "Creating rules for ports 22,80,443 in security group ${SECGROUP_NAME}"
378-
os_cmd security group rule create --proto icmp ${SECGROUP_NAME}
379-
os_cmd security group rule create --proto tcp --dst-port 22 ${SECGROUP_NAME}
380-
os_cmd security group rule create --proto tcp --dst-port 80 ${SECGROUP_NAME}
381-
os_cmd security group rule create --proto tcp --dst-port 443 ${SECGROUP_NAME}
464+
os_cmd security group rule create --proto ${ICMP_PROTO} ${ETHERTYPE_ARG} ${SECGROUP_NAME}
465+
os_cmd security group rule create --proto tcp --dst-port 22 ${ETHERTYPE_ARG} ${SECGROUP_NAME}
466+
os_cmd security group rule create --proto tcp --dst-port 80 ${ETHERTYPE_ARG} ${SECGROUP_NAME}
467+
os_cmd security group rule create --proto tcp --dst-port 443 ${ETHERTYPE_ARG} ${SECGROUP_NAME}
382468
fi
383469

384470
## create sriov port
@@ -394,25 +480,39 @@ function workload_launch {
394480

395481

396482
## create instance
397-
TENANT_NET_ID=$( openstack network show -f value -c id "${TENANT_NET_NAME}" )
398-
399-
echo "Creating overcloud instance ${INSTANCE_NAME}"
400-
{% if workload_sriov|default(false) | bool -%}
401-
os_cmd server create \
402-
--image "${IMAGE_NAME}" \
403-
--flavor "${FLAVOR_NAME}" \
404-
--key-name "${KEYPAIR_NAME}" \
405-
--port "${SRIOV_PORT}" \
406-
"${INSTANCE_NAME}"
407-
{% else -%}
408-
os_cmd server create \
409-
--image "${IMAGE_NAME}" \
410-
--flavor "${FLAVOR_NAME}" \
411-
--security-group "${SECGROUP_NAME}" \
412-
--key-name "${KEYPAIR_NAME}" \
413-
--nic net-id="${TENANT_NET_ID}" \
414-
"${INSTANCE_NAME}"
415-
{%- endif %}
483+
if [ "${IS_IPV6}" = "True" ]; then
484+
# For IPv6, attach directly to public network
485+
PUBLIC_NET_ID=$(openstack network show -f value -c id "${EXTERNAL_NET_NAME}")
486+
echo "Creating overcloud instance ${INSTANCE_NAME} on public network (IPv6)"
487+
# Note: SRIOV unsupported in IPv6 only setup (validated in prepare_env)
488+
os_cmd server create \
489+
--image "${IMAGE_NAME}" \
490+
--flavor "${FLAVOR_NAME}" \
491+
--security-group "${SECGROUP_NAME}" \
492+
--key-name "${KEYPAIR_NAME}" \
493+
--nic net-id="${PUBLIC_NET_ID}" \
494+
"${INSTANCE_NAME}"
495+
else
496+
# For IPv4, use tenant network (original behavior)
497+
TENANT_NET_ID=$( openstack network show -f value -c id "${TENANT_NET_NAME}" )
498+
echo "Creating overcloud instance ${INSTANCE_NAME} on tenant network (IPv4)"
499+
{% if workload_sriov|default(false) | bool -%}
500+
os_cmd server create \
501+
--image "${IMAGE_NAME}" \
502+
--flavor "${FLAVOR_NAME}" \
503+
--key-name "${KEYPAIR_NAME}" \
504+
--port "${SRIOV_PORT}" \
505+
"${INSTANCE_NAME}"
506+
{% else -%}
507+
os_cmd server create \
508+
--image "${IMAGE_NAME}" \
509+
--flavor "${FLAVOR_NAME}" \
510+
--security-group "${SECGROUP_NAME}" \
511+
--key-name "${KEYPAIR_NAME}" \
512+
--nic net-id="${TENANT_NET_ID}" \
513+
"${INSTANCE_NAME}"
514+
{% endif %}
515+
fi
416516

417517
elapsed_seconds=0
418518
while true; do
@@ -536,5 +636,10 @@ if [[ "${MODE}" == "sanityfast" ]]; then
536636
SUFFIX=$(openssl rand -hex 5)
537637
prepare_env
538638
sanity_check $FAST
539-
workload_launch $FAST $FAST
639+
if [ "${IS_IPV6}" = "True" ]; then
640+
# IPv6 needs longer SSH timeout due to DHCP timeout, but keep boot timeout fast
641+
workload_launch $FAST 180
642+
else
643+
workload_launch $FAST $FAST
644+
fi
540645
fi

0 commit comments

Comments
 (0)