2 # Common Functions used by OPNFV Apex
3 # author: Tim Rozet (trozet@redhat.com)
6 ip_gen="python3.4 -B -m apex.ip_utils generate_ip_range"
8 ##converts subnet mask to prefix
10 function prefix2mask {
11 # Number of args to shift, 255..255, first non-255 byte, zeroes
12 set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
13 [ $1 -gt 1 ] && shift $1 || shift
14 echo ${1-0}.${2-0}.${3-0}.${4-0}
17 ##find ip of interface
18 ##params: interface name
20 ip addr show $1 | grep -Eo '^\s+inet\s+[\.0-9]+' | awk '{print $2}'
23 ##finds subnet of ip and netmask
25 function find_subnet {
26 IFS=. read -r i1 i2 i3 i4 <<< "$1"
27 IFS=. read -r m1 m2 m3 m4 <<< "$2"
28 printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))"
31 ##verify subnet has at least n IPs
32 ##params: subnet mask, n IPs
33 function verify_subnet_size {
34 IFS=. read -r i1 i2 i3 i4 <<< "$1"
37 ##this function assumes you would never need more than 254
38 ##we check here to make sure
39 if [ "$num_ips_required" -ge 254 ]; then
40 echo -e "\n\n${red}ERROR: allocating more than 254 IPs is unsupported...Exiting${reset}\n\n"
44 ##we just return if 3rd octet is not 255
45 ##because we know the subnet is big enough
46 if [ "$i3" -ne 255 ]; then
48 elif [ $((254-$i4)) -ge "$num_ips_required" ]; then
51 echo -e "\n\n${red}ERROR: Subnet is too small${reset}\n\n"
56 ##finds last usable ip (broadcast minus 1) of a subnet from an IP and netmask
57 ## Warning: This function only works for IPv4 at the moment.
59 function find_last_ip_subnet {
60 IFS=. read -r i1 i2 i3 i4 <<< "$1"
61 IFS=. read -r m1 m2 m3 m4 <<< "$2"
62 IFS=. read -r s1 s2 s3 s4 <<< "$((i1 & m1)).$((i2 & m2)).$((i3 & m3)).$((i4 & m4))"
63 printf "%d.%d.%d.%d\n" "$((255 - $m1 + $s1))" "$((255 - $m2 + $s2))" "$((255 - $m3 + $s3))" "$((255 - $m4 + $s4 - 1))"
66 ##increments subnet by a value
69 function increment_subnet {
70 IFS=. read -r i1 i2 i3 i4 <<< "$1"
71 printf "%d.%d.%d.%d\n" "$i1" "$i2" "$i3" "$((i4 | $2))"
74 ##finds netmask of interface
76 ##returns long format 255.255.x.x
77 function find_netmask {
78 ifconfig $1 | grep -Eo 'netmask\s+[\.0-9]+' | awk '{print $2}'
81 ##finds short netmask of interface
83 ##returns short format, ex: /21
84 function find_short_netmask {
85 echo "/$(ip addr show $1 | grep -Eo '^\s+inet\s+[\/\.0-9]+' | awk '{print $2}' | cut -d / -f2)"
90 ##assumes a /24 subnet
92 baseaddr="$(echo $1 | cut -d. -f1-3)"
93 lsv="$(echo $1 | cut -d. -f4)"
94 if [ "$lsv" -ge 254 ]; then
101 ##subtracts a value from an IP address
102 ##params: last ip, ip_count
103 ##assumes ip_count is less than the last octect of the address
105 IFS=. read -r i1 i2 i3 i4 <<< "$1"
107 if [ $i4 -lt $ip_count ]; then
108 echo -e "\n\n${red}ERROR: Can't subtract $ip_count from IP address $1 Exiting${reset}\n\n"
111 printf "%d.%d.%d.%d\n" "$i1" "$i2" "$i3" "$((i4 - $ip_count ))"
114 ##check if IP is in use
116 ##ping ip to get arp entry, then check arp
117 function is_ip_used {
118 ping -c 5 $1 > /dev/null 2>&1
119 arp -n | grep "$1 " | grep -iv incomplete > /dev/null 2>&1
122 ##find next usable IP
124 function next_usable_ip {
126 while [ "$new_ip" ]; do
127 if ! is_ip_used $new_ip; then
131 new_ip=$(next_ip $new_ip)
136 ##increment ip by value
137 ##params: ip, amount to increment by
138 ##increment_ip $next_private_ip 10
139 function increment_ip {
140 baseaddr="$(echo $1 | cut -d. -f1-3)"
141 lsv="$(echo $1 | cut -d. -f4)"
144 if [ "$lsv" -ge 254 ]; then
150 ##finds gateway on system
151 ##params: interface to validate gateway on (optional)
153 function find_gateway {
154 local gw gw_interface
158 gw=$(ip route | grep default | awk '{print $3}')
159 gw_interface=$(ip route get $gw | awk '{print $3}')
161 if [ "$gw_interface" == "$1" ]; then
167 ##finds subnet in CIDR notation for interface
168 ##params: interface to find CIDR
170 local cidr network ip netmask short_mask
175 netmask=$(find_netmask $1)
176 if [[ -z "$ip" || -z "$netmask" ]]; then
179 network=$(find_subnet ${ip} ${netamsk})
180 short_mask=$(find_short_netmask $1)
181 if [[ -z "$network" || -z "$short_mask" ]]; then
184 cidr="${subnet}'\'${short_mask}"
188 ##finds block of usable IP addresses for an interface
189 ##simply returns at the moment the correct format
190 ##after first 20 IPs, and leave 20 IPs at end of subnet (for floating ips, etc)
191 ##params: interface to find IP
192 function find_usable_ip_range {
193 local interface_ip subnet_mask first_block_ip last_block_ip
197 interface_ip=$(find_ip $1)
198 subnet_mask=$(find_netmask $1)
199 if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
202 interface_ip=$(increment_ip ${interface_ip} 20)
203 first_block_ip=$(next_usable_ip ${interface_ip})
204 if [ -z "$first_block_ip" ]; then
207 last_block_ip=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
208 if [ -z "$last_block_ip" ]; then
211 last_block_ip=$(subtract_ip ${last_block_ip} 21)
212 echo "${first_block_ip},${last_block_ip}"
217 ##generates usable IP range in correct format based on CIDR
218 ##A block of 20 IP addresses are reserved at beginning of address space.
219 ##A block of 22 IP addresses are reserved at end of address space, this includes
220 ##the broadcast IP address.
221 ##In a /24 IPv4 CIDR, this results in .1-20 as as .234-255 being excluded.
223 function generate_usable_ip_range {
227 echo $($ip_gen $1 21 -23)
231 ##find the undercloud IP address
232 ##finds first usable IP on subnet
234 function find_provisioner_ip {
239 interface_ip=$(find_ip $1)
240 if [ -z "$interface_ip" ]; then
243 echo $(increment_ip ${interface_ip} 1)
246 ##generates undercloud IP address based on CIDR
248 function generate_provisioner_ip {
252 echo $($ip_gen $1 1 1)
256 ##finds the dhcp range available via interface
257 ##uses first 8 IPs, after 2nd IP
259 function find_dhcp_range {
260 local dhcp_range_start dhcp_range_end interface_ip
264 interface_ip=$(find_ip $1)
265 if [ -z "$interface_ip" ]; then
268 dhcp_range_start=$(increment_ip ${interface_ip} 2)
269 dhcp_range_end=$(increment_ip ${dhcp_range_start} 8)
270 echo "${dhcp_range_start},${dhcp_range_end}"
273 ##generates the dhcp range available via CIDR
274 ##uses first 8 IPs, after 1st IP
276 function generate_dhcp_range {
280 echo $($ip_gen $1 2 10)
283 ##finds the introspection range available via interface
284 ##uses 8 IPs, after the first 10 IPs
286 function find_introspection_range {
287 local inspect_range_start inspect_range_end interface_ip
291 interface_ip=$(find_ip $1)
292 if [ -z "$interface_ip" ]; then
295 inspect_range_start=$(increment_ip ${interface_ip} 10)
296 inspect_range_end=$(increment_ip ${inspect_range_start} 8)
297 echo "${inspect_range_start},${inspect_range_end}"
300 ##generate the introspection range available via CIDR
301 ##uses 8 IPs, after the first 10 IPs
303 function generate_introspection_range {
307 echo $($ip_gen $1 11 19)
310 ##finds the floating ip range available via interface
311 ##uses last 20 IPs of a subnet, minus last IP
313 function find_floating_ip_range {
314 local float_range_start float_range_end interface_ip subnet_mask
318 interface_ip=$(find_ip $1)
319 subnet_mask=$(find_netmask $1)
320 if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
323 float_range_end=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
324 float_range_end=$(subtract_ip ${float_range_end} 1)
325 float_range_start=$(subtract_ip ${float_range_end} 19)
326 echo "${float_range_start},${float_range_end}"
329 ##generate the floating range available via CIDR
330 ##uses last 20 IPs of subnet, minus last 2 IPs.
331 ##In a /24 IPv4 CIDR, this would result in floating ip range of .234-253
333 function generate_floating_ip_range {
337 echo $($ip_gen $1 -22 -3)
340 ##attach interface to OVS and set the network config correctly
341 ##params: bride to attach to, interface to attach, network type (optional)
342 ##public indicates attaching to a public interface
343 function attach_interface_to_ovs {
344 local bridge interface
345 local if_ip if_mask if_gw if_file ovs_file if_prefix
347 if [[ -z "$1" || -z "$2" ]]; then
354 if ovs-vsctl list-ports ${bridge} | grep ${interface}; then
358 if_file=/etc/sysconfig/network-scripts/ifcfg-${interface}
359 ovs_file=/etc/sysconfig/network-scripts/ifcfg-${bridge}
361 if [ -e "$if_file" ]; then
362 if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file})
363 if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file})
364 if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file})
366 echo "ERROR: ifcfg file missing for ${interface}"
370 if [ -z "$if_mask" ]; then
371 # we can look for PREFIX here, then convert it to NETMASK
372 if_prefix=$(sed -n 's/^PREFIX=\(.*\)$/\1/p' ${if_file})
373 if_mask=$(prefix2mask ${if_prefix})
376 if [[ -z "$if_ip" || -z "$if_mask" ]]; then
377 echo "ERROR: IPADDR or NETMASK/PREFIX missing for ${interface}"
379 elif [[ -z "$if_gw" && "$3" == "public_network" ]]; then
380 echo "ERROR: GATEWAY missing for ${interface}, which is public"
384 # move old config file to .orig
385 mv -f ${if_file} ${if_file}.orig
386 echo "DEVICE=${interface}
394 PROMISC=yes" > ${if_file}
396 if [ -z ${if_gw} ]; then
398 echo "DEVICE=${bridge}
406 PEERDNS=no" > ${ovs_file}
409 echo "DEVICE=${bridge}
418 PEERDNS=no" > ${ovs_file}
421 sudo systemctl restart network
424 ##detach interface from OVS and set the network config correctly
425 ##params: bridge to detach from
426 ##assumes only 1 real interface attached to OVS
427 function detach_interface_from_ovs {
429 local port_output ports_no_orig
431 local if_ip if_mask if_gw if_prefix
433 net_path=/etc/sysconfig/network-scripts/
434 if [[ -z "$1" ]]; then
440 # if no interfaces attached then return
441 if ! ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*"; then
445 # look for .orig ifcfg files to use
446 port_output=$(ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*")
447 while read -r line; do
448 if [ -z "$line" ]; then
450 elif [ -e ${net_path}/ifcfg-${line}.orig ]; then
451 mv -f ${net_path}/ifcfg-${line}.orig ${net_path}/ifcfg-${line}
452 elif [ -e ${net_path}/ifcfg-${bridge} ]; then
453 if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file})
454 if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file})
455 if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file})
457 if [ -z "$if_mask" ]; then
458 if_prefix=$(sed -n 's/^PREFIX=\(.*\)$/\1/p' ${if_file})
459 if_mask=$(prefix2mask ${if_prefix})
462 if [[ -z "$if_ip" || -z "$if_mask" ]]; then
463 echo "ERROR: IPADDR or PREFIX/NETMASK missing for ${bridge} and no .orig file for interface ${line}"
467 if [ -z ${if_gw} ]; then
476 PEERDNS=no" > ${net_path}/ifcfg-${line}
486 PEERDNS=no" > ${net_path}/ifcfg-${line}
490 echo "ERROR: Real interface ${line} attached to bridge, but no interface or ${bridge} ifcfg file exists"
494 done <<< "$port_output"
496 # modify the bridge ifcfg file
497 # to remove IP params
498 sudo sed -i 's/IPADDR=.*//' ${net_path}/ifcfg-${bridge}
499 sudo sed -i 's/NETMASK=.*//' ${net_path}/ifcfg-${bridge}
500 sudo sed -i 's/GATEWAY=.*//' ${net_path}/ifcfg-${bridge}
502 sudo systemctl restart network
505 # Update iptables rule for external network reach internet
506 # for virtual deployments
507 # params: external_cidr
508 function configure_undercloud_nat {
510 if [[ -z "$1" ]]; then
516 ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" <<EOI
517 iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
518 iptables -t nat -A POSTROUTING -s ${external_cidr} -o eth0 -j MASQUERADE
519 iptables -A FORWARD -i eth2 -j ACCEPT
520 iptables -A FORWARD -s ${external_cidr} -m state --state ESTABLISHED,RELATED -j ACCEPT
521 service iptables save
525 # Interactive prompt handler
526 # params: step stage, ex. deploy, undercloud install, etc
527 function prompt_user {
529 echo -n "Would you like to proceed with ${1}? (y/n) "
531 if [ "$response" == 'y' ]; then
533 elif [ "$response" == 'n' ]; then