@@ -43,9 +43,25 @@ function os_cmd {
4343function 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
152179function 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 {
283334function 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
540645fi
0 commit comments