@@ -43,9 +43,18 @@ 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+ VM_IP=$( os_cmd server show ${INSTANCE_NAME} -f value -c addresses | grep -oE " 2620:[^']+" | head -1)
53+ if [ -z " ${VM_IP} " ]; then
54+ echo " Cannot get IPv6 address from instance"
55+ exit 66
56+ fi
57+ echo " Using direct IPv6 address: ${VM_IP} "
4958 else
5059 INSTANCE_FIP=$( os_cmd floating ip create -f value -c floating_ip_address " ${EXTERNAL_NET_NAME} " )
5160 if [ -z " ${INSTANCE_FIP} " ]; then
@@ -146,7 +155,19 @@ function prepare_env {
146155 export INSTANCE_FILE=" ${HOME} /{{ cifmw_update_artifacts_basedir_suffix }}/vm_info.sh"
147156 export WORKLOAD_FILE=" ${HOME} /{{ cifmw_update_artifacts_basedir_suffix }}/workload_suffix"
148157 export SSH_KEY_FILE=" ${HOME} /.ssh/${KEYPAIR_NAME} "
158+ # Detect IPv6 subnet dynamically at runtime
159+ export IPV6_SUBNET_ID=$( openstack subnet list --network " ${EXTERNAL_NET_NAME} " --ip-version 6 -f value -c ID | head -1)
160+ export IS_IPV6=$( [ -n " ${IPV6_SUBNET_ID} " ] && echo " True" || echo " False" )
161+ export IPV6_PORT_NAME=" ipv6_port_${SUFFIX} "
162+ export IPV6_PORT_ID=" " # Track port ID for IPv6 cleanup
149163 mkdir -p " ${HOME} /{{ cifmw_update_artifacts_basedir_suffix }}"
164+
165+ # Validate SRIOV is not used with IPv6 (untested configuration)
166+ local workload_sriov={{workload_sriov| default(false) | bool | ternary(" True" , " " )}}
167+ if [ " ${IS_IPV6} " = " True" ] && [ -n " ${workload_sriov} " ]; then
168+ echo " ERROR: SRIOV configuration is not supported with IPv6 deployments (untested)"
169+ exit 1
170+ fi
150171}
151172
152173function sanity_check {
@@ -187,20 +208,42 @@ function sanity_teardown {
187208
188209 local timeout_seconds=${1:- 180}
189210 local elapsed_seconds=0
211+
190212 {% if workload_sriov| default(false) | bool -%}
191- openstack port delete " ${SRIOV_PORT} "
213+ if [ -n " ${SRIOV_PORT} " ]; then
214+ echo " Deleting SRIOV port ${SRIOV_PORT} "
215+ openstack port delete " ${SRIOV_PORT} " || echo " Warning: Failed to delete SRIOV port ${SRIOV_PORT} "
216+ fi
192217 {% elif workload_dpdk| default(false) | bool -%}
193- openstack port delete " ${DPDK_PORT} "
218+ if [ -n " ${DPDK_PORT} " ]; then
219+ echo " Deleting DPDK port ${DPDK_PORT} "
220+ openstack port delete " ${DPDK_PORT} " || echo " Warning: Failed to delete DPDK port ${DPDK_PORT} "
221+ fi
194222 {% 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} "
223+ if [ " ${IS_IPV6} " = " True" ]; then
224+ # For IPv6, clean up the dedicated port
225+ if [ -n " ${IPV6_PORT_ID} " ]; then
226+ echo " IPv6 deployment - deleting port ${IPV6_PORT_NAME} (${IPV6_PORT_ID} )"
227+ openstack port delete " ${IPV6_PORT_ID} " || echo " Warning: Failed to delete port ${IPV6_PORT_ID} "
228+ elif [ -n " ${IPV6_PORT_NAME} " ]; then
229+ echo " IPv6 deployment - deleting port ${IPV6_PORT_NAME} by name"
230+ openstack port delete " ${IPV6_PORT_NAME} " || echo " Warning: Failed to delete port ${IPV6_PORT_NAME} "
231+ else
232+ echo " IPv6 deployment - no port to clean up"
233+ fi
234+ else
235+ # For IPv4, clean up floating IP
236+ if [ -n " ${INSTANCE_FIP} " ]; then
237+ echo " Remove ${INSTANCE_FIP} from ${INSTANCE_NAME} "
238+ openstack server remove floating ip ${INSTANCE_NAME} ${INSTANCE_FIP} || echo " Warning: Failed to remove floating IP from instance"
239+ if [ -f " ${INSTANCE_FILE} " ] && grep -q " ${INSTANCE_FIP} " " ${INSTANCE_FILE} " ; then
240+ rm " ${INSTANCE_FILE} "
241+ fi
199242
200- echo " Delete floating ip ${INSTANCE_FIP} "
201- openstack floating ip delete ${INSTANCE_FIP}
243+ echo " Delete floating ip ${INSTANCE_FIP} "
244+ openstack floating ip delete ${INSTANCE_FIP} || echo " Warning: Failed to delete floating IP ${INSTANCE_FIP} "
245+ fi
202246 fi
203-
204247 {%- endif %}
205248
206249 {% if cifmw_update_create_volume | default(True) | bool -%}
@@ -248,20 +291,25 @@ function sanity_teardown {
248291 fi
249292 {%- endif %}
250293
251- echo " Clear default gateway from ${TENANT_NET_NAME} _router"
252- openstack router unset --external-gateway " ${TENANT_NET_NAME} " _router
294+ # Only clean up tenant network resources if they were created (IPv4 mode)
295+ if [ " ${IS_IPV6} " != " True" ]; then
296+ echo " Clear default gateway from ${TENANT_NET_NAME} _router"
297+ openstack router unset --external-gateway " ${TENANT_NET_NAME} " _router || echo " Warning: Failed to clear gateway"
253298
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
299+ echo " Remove subnet ${TENANT_NET_NAME} _subnet from router ${TENANT_NET_NAME} _router"
300+ openstack router remove subnet " ${TENANT_NET_NAME} " _router " ${TENANT_NET_NAME} " _subnet || echo " Warning: Failed to remove subnet from router "
256301
257- echo " Remove router ${TENANT_NET_NAME} _router"
258- openstack router delete " ${TENANT_NET_NAME} _router"
302+ echo " Remove router ${TENANT_NET_NAME} _router"
303+ openstack router delete " ${TENANT_NET_NAME} _router" || echo " Warning: Failed to delete router "
259304
260- echo " Remove subnet ${TENANT_NET_NAME} _subnet"
261- openstack subnet delete " ${TENANT_NET_NAME} _subnet"
305+ echo " Remove subnet ${TENANT_NET_NAME} _subnet"
306+ openstack subnet delete " ${TENANT_NET_NAME} _subnet" || echo " Warning: Failed to delete subnet "
262307
263- echo " Remove network ${TENANT_NET_NAME} "
264- openstack network delete " ${TENANT_NET_NAME} "
308+ echo " Remove network ${TENANT_NET_NAME} "
309+ openstack network delete " ${TENANT_NET_NAME} " || echo " Warning: Failed to delete network"
310+ else
311+ echo " IPv6 deployment - skipping tenant network cleanup (using public network directly, bootstrap router persists)"
312+ fi
265313
266314 echo " Remove security group ${SECGROUP_NAME} "
267315 openstack security group delete " ${SECGROUP_NAME} "
@@ -283,7 +331,7 @@ function sanity_teardown {
283331function workload_launch {
284332 # create workload
285333 timeout_seconds=${1:- 120}
286- ssh_timeout_seconds=${1 :- 180}
334+ ssh_timeout_seconds=${2 :- 180}
287335 local workload_sriov={{workload_sriov| default(false) | bool | ternary(" True" , " " )}}
288336 local workload_dpdk={{workload_dpdk| default(false) | bool | ternary(" True" , " " )}}
289337
@@ -338,47 +386,82 @@ function workload_launch {
338386 fi
339387 fi
340388
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
389+ # # create networking for IPv4
390+ if [ " ${IS_IPV6} " != " True" ]; then
391+ openstack network list | grep ${TENANT_NET_NAME}
392+ if [ $? -ne 0 ]; then
393+ # Collect all nameservers from resolv.conf
394+ NAMESERVERS=$( awk ' /^nameserver[[:space:]]/ {print $2}' /etc/resolv.conf)
395+
396+ # Build DNS arguments
397+ DNS_ARGS=" "
398+ if [ -n " $NAMESERVERS " ]; then
399+ for ns in $NAMESERVERS ; do
400+ DNS_ARGS=" $DNS_ARGS --dns-nameserver $ns "
401+ done
402+ else
403+ echo " Warning: No nameservers found, using public DNS fallback"
404+ DNS_ARGS=" --dns-nameserver 8.8.8.8"
405+ fi
406+ echo " Creating router ${TENANT_NET_NAME} _router"
407+ os_cmd router create ${TENANT_NET_NAME} _router
408+
409+ echo " Creating network ${TENANT_NET_NAME} "
410+ if [ -n " ${workload_dpdk} " ]; then
411+ os_cmd network create ${TENANT_NET_NAME} --provider-network-type geneve
412+ else
413+ os_cmd network create ${TENANT_NET_NAME}
414+ fi
347415
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
416+ echo " Creating subnet ${TENANT_NET_NAME} _subnet"
417+ os_cmd subnet create \
418+ --subnet-range 192.168.0.0/24 \
419+ --allocation-pool start=192.168.0.10,end=192.168.0.100 \
420+ --gateway 192.168.0.254 \
421+ $DNS_ARGS \
422+ --network ${TENANT_NET_NAME} \
423+ ${TENANT_NET_NAME} _subnet
354424
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
425+ echo " Add subnet ${TENANT_NET_NAME} _subnet to router ${TENANT_NET_NAME} _router"
426+ os_cmd router add subnet ${TENANT_NET_NAME} _router ${TENANT_NET_NAME} _subnet
363427
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
428+ echo " Set external-gateway for ${TENANT_NET_NAME} _router"
429+ os_cmd router set --external-gateway ${EXTERNAL_NET_NAME} ${TENANT_NET_NAME} _router
430+ fi
431+ else
432+ # For IPv6, ensure OVN Router Advertisement bootstrap router exists
433+ # This is infrastructure-level and persists across all workload operations
434+ openstack router list | grep " ovn-ipv6-bootstrap-router"
435+ if [ $? -ne 0 ]; then
436+ echo " Creating OVN IPv6 bootstrap router (infrastructure-level, required for SLAAC)"
437+ os_cmd router create ovn-ipv6-bootstrap-router
366438
367- echo " Set external-gateway for ${TENANT_NET_NAME} _router"
368- os_cmd router set --external-gateway ${EXTERNAL_NET_NAME} ${TENANT_NET_NAME} _router
439+ echo " Set external-gateway for OVN IPv6 bootstrap router"
440+ os_cmd router set --external-gateway ${EXTERNAL_NET_NAME} ovn-ipv6-bootstrap-router
441+ fi
369442 fi
370-
371443 # # create security group
372444 openstack security group list | grep ${SECGROUP_NAME}
373445 if [ $? -ne 0 ]; then
374446 echo " Creating security group ${SECGROUP_NAME} "
375447 os_cmd security group create ${SECGROUP_NAME}
376448
449+ # Set protocol and ethertype based on IP version
450+ if [ " ${IS_IPV6} " = " True" ]; then
451+ ICMP_PROTO=" ipv6-icmp"
452+ ETHERTYPE_ARG=" --ethertype IPv6"
453+ echo " Creating IPv6 rules for ports 22,80,443 in security group ${SECGROUP_NAME} "
454+ else
455+ ICMP_PROTO=" icmp"
456+ ETHERTYPE_ARG=" "
457+ echo " Creating IPv4 rules for ports 22,80,443 in security group ${SECGROUP_NAME} "
458+ fi
459+
377460 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}
461+ os_cmd security group rule create --proto ${ICMP_PROTO} ${ETHERTYPE_ARG} ${SECGROUP_NAME}
462+ os_cmd security group rule create --proto tcp --dst-port 22 ${ETHERTYPE_ARG} ${ SECGROUP_NAME}
463+ os_cmd security group rule create --proto tcp --dst-port 80 ${ETHERTYPE_ARG} ${ SECGROUP_NAME}
464+ os_cmd security group rule create --proto tcp --dst-port 443 ${ETHERTYPE_ARG} ${ SECGROUP_NAME}
382465 fi
383466
384467 # # create sriov port
@@ -392,27 +475,55 @@ function workload_launch {
392475 openstack port create --vnic-type normal --network " ${TENANT_NET_NAME=} " " ${DPDK_PORT} "
393476 {%- endif %}
394477
478+ # # create IPv6 port for dual-stack deployments
479+ if [ " ${IS_IPV6} " = " True" ]; then
480+ echo " Creating IPv6 port ${IPV6_PORT_NAME} on public network"
481+ IPV6_PORT_ID=$( os_cmd port create \
482+ --network " ${EXTERNAL_NET_NAME} " \
483+ --fixed-ip subnet=" ${IPV6_SUBNET_ID} " \
484+ --security-group " ${SECGROUP_NAME} " \
485+ " ${IPV6_PORT_NAME} " -f value -c id)
486+
487+ if [ -z " ${IPV6_PORT_ID} " ]; then
488+ echo " Failed to create IPv6 port"
489+ exit 1
490+ fi
491+ echo " Created IPv6 port ${IPV6_PORT_NAME} with ID ${IPV6_PORT_ID} "
492+ fi
395493
396- # # create instance
397- TENANT_NET_ID=$( openstack network show -f value -c id " ${TENANT_NET_NAME} " )
398494
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 %}
495+ # # create instance
496+ if [ " ${IS_IPV6} " = " True" ]; then
497+ # For IPv6, use the pre-created port with IPv6 subnet constraint
498+ echo " Creating overcloud instance ${INSTANCE_NAME} with IPv6 port (dual-stack)"
499+ # Note: SRIOV unsupported in IPv6 only setup (validated in prepare_env)
500+ os_cmd server create \
501+ --image " ${IMAGE_NAME} " \
502+ --flavor " ${FLAVOR_NAME} " \
503+ --key-name " ${KEYPAIR_NAME} " \
504+ --port " ${IPV6_PORT_NAME} " \
505+ " ${INSTANCE_NAME} "
506+ else
507+ # For IPv4, use tenant network (original behavior)
508+ TENANT_NET_ID=$( openstack network show -f value -c id " ${TENANT_NET_NAME} " )
509+ echo " Creating overcloud instance ${INSTANCE_NAME} on tenant network (IPv4)"
510+ {% if workload_sriov| default(false) | bool -%}
511+ os_cmd server create \
512+ --image " ${IMAGE_NAME} " \
513+ --flavor " ${FLAVOR_NAME} " \
514+ --key-name " ${KEYPAIR_NAME} " \
515+ --port " ${SRIOV_PORT} " \
516+ " ${INSTANCE_NAME} "
517+ {% else -%}
518+ os_cmd server create \
519+ --image " ${IMAGE_NAME} " \
520+ --flavor " ${FLAVOR_NAME} " \
521+ --security-group " ${SECGROUP_NAME} " \
522+ --key-name " ${KEYPAIR_NAME} " \
523+ --nic net-id=" ${TENANT_NET_ID} " \
524+ " ${INSTANCE_NAME} "
525+ {% endif %}
526+ fi
416527
417528 elapsed_seconds=0
418529 while true ; do
@@ -498,7 +609,12 @@ if [[ "${MODE}" == "workload" ]]; then
498609 {% if cifmw_update_create_volume | default(true) | bool -%}
499610 echo " export CINDER_VOL_ID=${CINDER_VOL_ID} " >> " ${WORKLOAD_FILE} "
500611 {% endif -%}
501- echo " export INSTANCE_FIP=${INSTANCE_FIP} " >> " ${WORKLOAD_FILE} "
612+ if [ " ${IS_IPV6} " = " True" ]; then
613+ echo " export IPV6_PORT_ID=${IPV6_PORT_ID} " >> " ${WORKLOAD_FILE} "
614+ echo " export IPV6_PORT_NAME=${IPV6_PORT_NAME} " >> " ${WORKLOAD_FILE} "
615+ else
616+ echo " export INSTANCE_FIP=${INSTANCE_FIP} " >> " ${WORKLOAD_FILE} "
617+ fi
502618fi
503619
504620if [[ " ${MODE} " == " workload_traffic" ]]; then
@@ -508,7 +624,12 @@ if [[ "${MODE}" == "workload_traffic" ]]; then
508624 workload_launch
509625 echo " export SUFFIX=${SUFFIX} " > ~ /workload_suffix
510626 echo " export CINDER_VOL_ID=${CINDER_VOL_ID} " >> ~ /workload_suffix
511- echo " export INSTANCE_FIP=${INSTANCE_FIP} " >> ~ /workload_suffix
627+ if [ " ${IS_IPV6} " = " True" ]; then
628+ echo " export IPV6_PORT_ID=${IPV6_PORT_ID} " >> ~ /workload_suffix
629+ echo " export IPV6_PORT_NAME=${IPV6_PORT_NAME} " >> ~ /workload_suffix
630+ else
631+ echo " export INSTANCE_FIP=${INSTANCE_FIP} " >> ~ /workload_suffix
632+ fi
512633 echo " export VM_IP=${VM_IP} " > ~ /vm_ip.sh
513634 generate_traffic
514635fi
@@ -536,5 +657,10 @@ if [[ "${MODE}" == "sanityfast" ]]; then
536657 SUFFIX=$( openssl rand -hex 5)
537658 prepare_env
538659 sanity_check $FAST
539- workload_launch $FAST $FAST
660+ if [ " ${IS_IPV6} " = " True" ]; then
661+ # IPv6 needs longer SSH timeout due to DHCP timeout, but keep boot timeout fast
662+ workload_launch $FAST 180
663+ else
664+ workload_launch $FAST $FAST
665+ fi
540666fi
0 commit comments