Enables private and storage networks
[apex.git] / lib / common-functions.sh
1 #!/usr/bin/env bash
2 # Common Functions used by  OPNFV Apex
3 # author: Tim Rozet (trozet@redhat.com)
4
5 ##find ip of interface
6 ##params: interface name
7 function find_ip {
8   ip addr show $1 | grep -Eo '^\s+inet\s+[\.0-9]+' | awk '{print $2}'
9 }
10
11 ##finds subnet of ip and netmask
12 ##params: ip, netmask
13 function find_subnet {
14   IFS=. read -r i1 i2 i3 i4 <<< "$1"
15   IFS=. read -r m1 m2 m3 m4 <<< "$2"
16   printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))"
17 }
18
19 ##verify subnet has at least n IPs
20 ##params: subnet mask, n IPs
21 function verify_subnet_size {
22   IFS=. read -r i1 i2 i3 i4 <<< "$1"
23   num_ips_required=$2
24
25   ##this function assumes you would never need more than 254
26   ##we check here to make sure
27   if [ "$num_ips_required" -ge 254 ]; then
28     echo -e "\n\n${red}ERROR: allocating more than 254 IPs is unsupported...Exiting${reset}\n\n"
29     return 1
30   fi
31
32   ##we just return if 3rd octet is not 255
33   ##because we know the subnet is big enough
34   if [ "$i3" -ne 255 ]; then
35     return 0
36   elif [ $((254-$i4)) -ge "$num_ips_required" ]; then
37     return 0
38   else
39     echo -e "\n\n${red}ERROR: Subnet is too small${reset}\n\n"
40     return 1
41   fi
42 }
43
44 ##finds last usable ip (broadcast minus 1) of a subnet from an IP and netmask
45 ## Warning: This function only works for IPv4 at the moment.
46 ##params: ip, netmask
47 function find_last_ip_subnet {
48   IFS=. read -r i1 i2 i3 i4 <<< "$1"
49   IFS=. read -r m1 m2 m3 m4 <<< "$2"
50   IFS=. read -r s1 s2 s3 s4 <<< "$((i1 & m1)).$((i2 & m2)).$((i3 & m3)).$((i4 & m4))"
51   printf "%d.%d.%d.%d\n" "$((255 - $m1 + $s1))" "$((255 - $m2 + $s2))" "$((255 - $m3 + $s3))" "$((255 - $m4 + $s4 - 1))"
52 }
53
54 ##increments subnet by a value
55 ##params: ip, value
56 ##assumes low value
57 function increment_subnet {
58   IFS=. read -r i1 i2 i3 i4 <<< "$1"
59   printf "%d.%d.%d.%d\n" "$i1" "$i2" "$i3" "$((i4 | $2))"
60 }
61
62 ##finds netmask of interface
63 ##params: interface
64 ##returns long format 255.255.x.x
65 function find_netmask {
66   ifconfig $1 | grep -Eo 'netmask\s+[\.0-9]+' | awk '{print $2}'
67 }
68
69 ##finds short netmask of interface
70 ##params: interface
71 ##returns short format, ex: /21
72 function find_short_netmask {
73   echo "/$(ip addr show $1 | grep -Eo '^\s+inet\s+[\/\.0-9]+' | awk '{print $2}' | cut -d / -f2)"
74 }
75
76 ##increments next IP
77 ##params: ip
78 ##assumes a /24 subnet
79 function next_ip {
80   baseaddr="$(echo $1 | cut -d. -f1-3)"
81   lsv="$(echo $1 | cut -d. -f4)"
82   if [ "$lsv" -ge 254 ]; then
83     return 1
84   fi
85   ((lsv++))
86   echo $baseaddr.$lsv
87 }
88
89 ##subtracts a value from an IP address
90 ##params: last ip, ip_count
91 ##assumes ip_count is less than the last octect of the address
92 subtract_ip() {
93   IFS=. read -r i1 i2 i3 i4 <<< "$1"
94   ip_count=$2
95   if [ $i4 -lt $ip_count ]; then
96     echo -e "\n\n${red}ERROR: Can't subtract $ip_count from IP address $1  Exiting${reset}\n\n"
97     exit 1
98   fi
99   printf "%d.%d.%d.%d\n" "$i1" "$i2" "$i3" "$((i4 - $ip_count ))"
100 }
101
102 ##check if IP is in use
103 ##params: ip
104 ##ping ip to get arp entry, then check arp
105 function is_ip_used {
106   ping -c 5 $1 > /dev/null 2>&1
107   arp -n | grep "$1 " | grep -iv incomplete > /dev/null 2>&1
108 }
109
110 ##find next usable IP
111 ##params: ip
112 function next_usable_ip {
113   new_ip=$(next_ip $1)
114   while [ "$new_ip" ]; do
115     if ! is_ip_used $new_ip; then
116       echo $new_ip
117       return 0
118     fi
119     new_ip=$(next_ip $new_ip)
120   done
121   return 1
122 }
123
124 ##increment ip by value
125 ##params: ip, amount to increment by
126 ##increment_ip $next_private_ip 10
127 function increment_ip {
128   baseaddr="$(echo $1 | cut -d. -f1-3)"
129   lsv="$(echo $1 | cut -d. -f4)"
130   incrval=$2
131   lsv=$((lsv+incrval))
132   if [ "$lsv" -ge 254 ]; then
133     return 1
134   fi
135   echo $baseaddr.$lsv
136 }
137
138 ##finds gateway on system
139 ##params: interface to validate gateway on (optional)
140 ##find_gateway em1
141 function find_gateway {
142   local gw gw_interface
143   if [ -z "$1"  ]; then
144     return 1
145   fi
146   gw=$(ip route | grep default | awk '{print $3}')
147   gw_interface=$(ip route get $gw | awk '{print $3}')
148   if [ -n "$1" ]; then
149     if [ "$gw_interface" == "$1" ]; then
150       echo ${gw}
151     fi
152   fi
153 }
154
155 ##finds subnet in CIDR notation for interface
156 ##params: interface to find CIDR
157 function find_cidr {
158   local cidr network ip netmask short_mask
159   if [ -z "$1"  ]; then
160     return 1
161   fi
162   ip=$(find_ip $1)
163   netmask=$(find_netmask $1)
164   if [[ -z "$ip" || -z "$netmask" ]]; then
165     return 1
166   fi
167   network=$(find_subnet ${ip} ${netamsk})
168   short_mask=$(find_short_netmask $1)
169   if [[ -z "$network" || -z "$short_mask" ]]; then
170     return 1
171   fi
172   cidr="${subnet}'\'${short_mask}"
173   echo ${cidr}
174 }
175
176 ##finds block of usable IP addresses for an interface
177 ##simply returns at the moment the correct format
178 ##after first 20 IPs, and leave 20 IPs at end of subnet (for floating ips, etc)
179 ##params: interface to find IP
180 function find_usable_ip_range {
181   local interface_ip subnet_mask first_block_ip last_block_ip
182   if [ -z "$1"  ]; then
183     return 1
184   fi
185   interface_ip=$(find_ip $1)
186   subnet_mask=$(find_netmask $1)
187   if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
188     return 1
189   fi
190   interface_ip=$(increment_ip ${interface_ip} 20)
191   first_block_ip=$(next_usable_ip ${interface_ip})
192   if [ -z "$first_block_ip" ]; then
193     return 1
194   fi
195   last_block_ip=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
196   if [ -z "$last_block_ip" ]; then
197     return 1
198   else
199     last_block_ip=$(subtract_ip ${last_block_ip} 21)
200     echo "${first_block_ip},${last_block_ip}"
201   fi
202
203 }
204
205 ##generates usable IP range in correct format based on CIDR
206 ##assumes the first 20 IPs are used (by instack or otherwise)
207 ##params: cidr
208 function generate_usable_ip_range {
209   local first_ip first_block_ip last_block_ip
210   #first_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
211   first_ip=$(ipcalc -nmpb $1 | grep NETWORK= | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
212   first_ip=$(increment_ip ${first_ip} 1)
213   first_block_ip=$(increment_ip ${first_ip} 20)
214   #last_block_ip=$(ipcalc  -nb $1 | grep HostMax: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
215   last_block_ip=$(ipcalc -nmpb $1 | grep BROADCAST= | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
216   last_block_ip=$(subtract_ip ${last_block_ip} 1)
217   if [[ -z "$first_block_ip" || -z "$last_block_ip" ]]; then
218     return 1
219   else
220     last_block_ip=$(subtract_ip ${last_block_ip} 21)
221     echo "${first_block_ip},${last_block_ip}"
222   fi
223 }
224
225 ##find the instack IP address
226 ##finds first usable IP on subnet
227 ##params: interface
228 function find_provisioner_ip {
229   local interface_ip
230   if [ -z "$1"  ]; then
231     return 1
232   fi
233   interface_ip=$(find_ip $1)
234   if [ -z "$interface_ip" ]; then
235     return 1
236   fi
237   echo $(increment_ip ${interface_ip} 1)
238 }
239
240 ##generates instack IP address based on CIDR
241 ##params: cidr
242 function generate_provisioner_ip {
243   local provisioner_ip
244   #provisioner_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
245   provisioner_ip=$(ipcalc -nmpb $1 | grep NETWORK= | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
246   if [ -z "$provisioner_ip" ]; then
247     return 1
248   fi
249   provisioner_ip=$(increment_ip ${provisioner_ip} 1)
250   echo "$provisioner_ip"
251 }
252
253 ##finds the dhcp range available via interface
254 ##uses first 8 IPs, after 2nd IP
255 ##params: interface
256 function find_dhcp_range {
257   local dhcp_range_start dhcp_range_end interface_ip
258   if [ -z "$1"  ]; then
259     return 1
260   fi
261   interface_ip=$(find_ip $1)
262   if [ -z "$interface_ip" ]; then
263     return 1
264   fi
265   dhcp_range_start=$(increment_ip ${interface_ip} 2)
266   dhcp_range_end=$(increment_ip ${dhcp_range_start} 8)
267   echo "${dhcp_range_start},${dhcp_range_end}"
268 }
269
270 ##generates the dhcp range available via CIDR
271 ##uses first 8 IPs, after 1st IP
272 ##params: cidr
273 function generate_dhcp_range {
274   local dhcp_range_start dhcp_range_end first_ip
275   #first_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
276   first_ip=$(ipcalc -nmpb $1 | grep NETWORK= | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
277   if [ -z "$first_ip" ]; then
278     return 1
279   fi
280   first_ip=$(increment_ip ${first_ip} 1)
281   dhcp_range_start=$(increment_ip ${first_ip} 1)
282   dhcp_range_end=$(increment_ip ${dhcp_range_start} 8)
283   echo "${dhcp_range_start},${dhcp_range_end}"
284 }
285
286 ##finds the introspection range available via interface
287 ##uses 8 IPs, after the first 10 IPs
288 ##params: interface
289 function find_introspection_range {
290   local inspect_range_start inspect_range_end interface_ip
291   if [ -z "$1"  ]; then
292     return 1
293   fi
294   interface_ip=$(find_ip $1)
295   if [ -z "$interface_ip" ]; then
296     return 1
297   fi
298   inspect_range_start=$(increment_ip ${interface_ip} 10)
299   inspect_range_end=$(increment_ip ${inspect_range_start} 8)
300   echo "${inspect_range_start},${inspect_range_end}"
301 }
302
303 ##generate the introspection range available via CIDR
304 ##uses 8 IPs, after the first 10 IPs
305 ##params: cidr
306 function generate_introspection_range {
307   local inspect_range_start inspect_range_end first_ip
308   #first_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
309   first_ip=$(ipcalc -nmpb $1 | grep NETWORK= | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
310   if [ -z "$first_ip" ]; then
311     return 1
312   fi
313   first_ip=$(increment_ip ${first_ip} 1)
314   inspect_range_start=$(increment_ip ${first_ip} 10)
315   inspect_range_end=$(increment_ip ${inspect_range_start} 8)
316   echo "${inspect_range_start},${inspect_range_end}"
317 }
318
319 ##finds the floating ip range available via interface
320 ##uses last 20 IPs of a subnet, minus last IP
321 ##params: interface
322 function find_floating_ip_range {
323   local float_range_start float_range_end interface_ip subnet_mask
324   if [ -z "$1"  ]; then
325     return 1
326   fi
327   interface_ip=$(find_ip $1)
328   subnet_mask=$(find_netmask $1)
329   if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
330     return 1
331   fi
332   float_range_end=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
333   float_range_end=$(subtract_ip ${float_range_end} 1)
334   float_range_start=$(subtract_ip ${float_range_end} 19)
335   echo "${float_range_start},${float_range_end}"
336 }
337
338 ##generate the floating range available via CIDR
339 ##uses last 20 IPs of subnet, minus last IP
340 ##params: cidr
341 function generate_floating_ip_range {
342   local float_range_start float_range_end last_ip
343   #last_ip=$(ipcalc  -nb $1 | grep HostMax: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
344   last_ip=$(ipcalc -nmpb $1 | grep BROADCAST= | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
345   if [ -z "$last_ip" ]; then
346     return 1
347   fi
348   last_ip=$(subtract_ip ${last_ip} 2)
349   float_range_start=$(subtract_ip ${last_ip} 19)
350   float_range_end=${last_ip}
351   echo "${float_range_start},${float_range_end}"
352 }
353
354 ##attach interface to OVS and set the network config correctly
355 ##params: bride to attach to, interface to attach, network type (optional)
356 ##public indicates attaching to a public interface
357 function attach_interface_to_ovs {
358   local bridge interface
359   local if_ip if_mask if_gw if_file ovs_file
360
361   if [[ -z "$1" || -z "$2" ]]; then
362     return 1
363   else
364     bridge=$1
365     interface=$2
366   fi
367
368   if ovs-vsctl list-ports ${bridge} | grep ${interface}; then
369     return 0
370   fi
371
372   if_file=/etc/sysconfig/network-scripts/ifcfg-${interface}
373   ovs_file=/etc/sysconfig/network-scripts/ifcfg-${bridge}
374
375   if [ -e "$if_file" ]; then
376     if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file})
377     if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file})
378     if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file})
379   else
380     echo "ERROR: ifcfg file missing for ${interface}"
381     return 1
382   fi
383
384   if [[ -z "$if_ip" || -z "$if_mask" ]]; then
385     echo "ERROR: IPADDR or NETMASK missing for ${interface}"
386     return 1
387   elif [[ -z "$if_gw" && "$3" == "public_network" ]]; then
388     echo "ERROR: GATEWAY missing for ${interface}, which is public"
389     return 1
390   fi
391
392   # move old config file to .orig
393   mv -f ${if_file} ${if_file}.orig
394   echo "DEVICE=${interface}
395 DEVICETYPE=ovs
396 TYPE=OVSPort
397 PEERDNS=no
398 BOOTPROTO=static
399 NM_CONTROLLED=no
400 ONBOOT=yes
401 OVS_BRIDGE=${bridge}
402 PROMISC=yes" > ${if_file}
403
404   if [ -z ${if_gw} ]; then
405   # create bridge cfg
406   echo "DEVICE=${bridge}
407 DEVICETYPE=ovs
408 IPADDR=${if_ip}
409 NETMASK=${if_mask}
410 BOOTPROTO=static
411 ONBOOT=yes
412 TYPE=OVSBridge
413 PROMISC=yes
414 PEERDNS=no" > ${ovs_file}
415
416   else
417     echo "DEVICE=${bridge}
418 DEVICETYPE=ovs
419 IPADDR=${if_ip}
420 NETMASK=${if_mask}
421 BOOTPROTO=static
422 ONBOOT=yes
423 TYPE=OVSBridge
424 PROMISC=yes
425 GATEWAY=${if_gw}
426 PEERDNS=no" > ${ovs_file}
427   fi
428
429   sudo systemctl restart network
430 }
431
432 ##detach interface from OVS and set the network config correctly
433 ##params: bridge to detach from
434 ##assumes only 1 real interface attached to OVS
435 function detach_interface_from_ovs {
436   local bridge
437   local port_output ports_no_orig
438   local net_path
439   local if_ip if_mask if_gw
440
441   net_path=/etc/sysconfig/network-scripts/
442   if [[ -z "$1" ]]; then
443     return 1
444   else
445     bridge=$1
446   fi
447
448   # if no interfaces attached then return
449   if ! ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*"; then
450     return 0
451   fi
452
453   # look for .orig ifcfg files  to use
454   port_output=$(ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*")
455   while read -r line; do
456     if [ -z "$line" ]; then
457       continue
458     elif [ -e ${net_path}/ifcfg-${line}.orig ]; then
459       mv -f ${net_path}/ifcfg-${line}.orig ${net_path}/ifcfg-${line}
460     elif [ -e ${net_path}/ifcfg-${bridge} ]; then
461       if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file})
462       if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file})
463       if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file})
464
465       if [[ -z "$if_ip" || -z "$if_mask" ]]; then
466         echo "ERROR: IPADDR or NETMASK missing for ${bridge} and no .orig file for interface ${line}"
467         return 1
468       fi
469
470       if [ -z ${if_gw} ]; then
471         # create if cfg
472         echo "DEVICE=${line}
473 IPADDR=${if_ip}
474 NETMASK=${if_mask}
475 BOOTPROTO=static
476 ONBOOT=yes
477 TYPE=Ethernet
478 NM_CONTROLLED=no
479 PEERDNS=no" > ${net_path}/ifcfg-${line}
480       else
481         echo "DEVICE=${line}
482 IPADDR=${if_ip}
483 NETMASK=${if_mask}
484 BOOTPROTO=static
485 ONBOOT=yes
486 TYPE=Ethernet
487 NM_CONTROLLED=no
488 GATEWAY=${if_gw}
489 PEERDNS=no" > ${net_path}/ifcfg-${line}
490       fi
491       break
492     else
493       echo "ERROR: Real interface ${line} attached to bridge, but no interface or ${bridge} ifcfg file exists"
494       return 1
495     fi
496
497   done <<< "$port_output"
498
499   # modify the bridge ifcfg file
500   # to remove IP params
501   sudo sed -i 's/IPADDR=.*//' ${net_path}/ifcfg-${bridge}
502   sudo sed -i 's/NETMASK=.*//' ${net_path}/ifcfg-${bridge}
503   sudo sed -i 's/GATEWAY=.*//' ${net_path}/ifcfg-${bridge}
504
505   sudo systemctl restart network
506 }