6738ddf43db9b7f54c501cf0bc49259db8426ef7
[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   gw=$(ip route | grep default | awk '{print $3}')
144   gw_interface=$(ip route get $gw | awk '{print $3}')
145   if [ -n "$1" ]; then
146     if [ "$gw_interface" == "$1" ]; then
147       echo ${gw}
148     fi
149   fi
150 }
151
152 ##finds subnet in CIDR notation for interface
153 ##params: interface to find CIDR
154 function find_cidr {
155   local cidr network ip netmask short_mask
156   ip=$(find_ip $1)
157   netmask=$(find_netmask $1)
158   if [[ -z "$ip" || -z "$netmask" ]]; then
159     return 1
160   fi
161   network=$(find_subnet ${ip} ${netamsk})
162   short_mask=$(find_short_netmask $1)
163   if [[ -z "$network" || -z "$short_mask" ]]; then
164     return 1
165   fi
166   cidr="${subnet}'\'${short_mask}"
167   echo ${cidr}
168 }
169
170 ##finds block of usable IP addresses for an interface
171 ##simply returns at the moment the correct format
172 ##after first 20 IPs, and leave 20 IPs at end of subnet (for floating ips, etc)
173 ##params: interface to find IP
174 function find_usable_ip_range {
175   local interface_ip subnet_mask first_block_ip last_block_ip
176   interface_ip=$(find_ip $1)
177   subnet_mask=$(find_netmask $1)
178   if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
179     return 1
180   fi
181   interface_ip=$(increment_ip ${interface_ip} 20)
182   first_block_ip=$(next_usable_ip ${interface_ip})
183   if [ -z "$first_block_ip" ]; then
184     return 1
185   fi
186   last_block_ip=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
187   if [ -z "$last_block_ip" ]; then
188     return 1
189   else
190     last_block_ip=$(subtract_ip ${last_block_ip} 20)
191     echo "${first_block_ip},${last_block_ip}"
192   fi
193
194 }
195
196 ##generates usable IP range in correct format based on CIDR
197 ##assumes the first 20 IPs are used (by instack or otherwise)
198 ##params: cidr
199 function generate_usable_ip_range {
200   local first_ip first_block_ip last_block_ip
201   first_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
202   first_block_ip=$(increment_ip ${first_ip} 20)
203   last_block_ip=$(ipcalc  -nb $1 | grep HostMax: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
204   if [[ -z "$first_block_ip" || -z "$last_block_ip" ]]; then
205     return 1
206   else
207     last_block_ip=$(subtract_ip ${last_block_ip} 20)
208     echo "${first_block_ip},${last_block_ip}"
209   fi
210 }
211
212 ##find the instack IP address
213 ##finds first usable IP on subnet
214 ##params: interface
215 function find_provisioner_ip {
216   local interface_ip
217   interface_ip=$(find_ip $1)
218   if [ -z "$interface_ip" ]; then
219     return 1
220   fi
221   echo $(increment_ip ${interface_ip} 1)
222 }
223
224 ##generates instack IP address based on CIDR
225 ##params: cidr
226 function generate_provisioner_ip {
227   local provisioner_ip
228   provisioner_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
229 }
230
231 ##finds the dhcp range available via interface
232 ##uses first 8 IPs, after 2nd IP
233 ##params: interface
234 function find_dhcp_range {
235   local dhcp_range_start dhcp_range_end interface_ip
236   interface_ip=$(find_ip $1)
237   if [ -z "$interface_ip" ]; then
238     return 1
239   fi
240   dhcp_range_start=$(increment_ip ${interface_ip} 2)
241   dhcp_range_end=$(increment_ip ${dhcp_range_start} 8)
242   echo "${dhcp_range_start},${dhcp_range_end}"
243 }
244
245 ##generates the dhcp range available via CIDR
246 ##uses first 8 IPs, after 1st IP
247 ##params: cidr
248 function generate_dhcp_range {
249   local dhcp_range_start dhcp_range_end first_ip
250   first_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
251   if [ -z "$first_ip" ]; then
252     return 1
253   fi
254   dhcp_range_start=$(increment_ip ${first_ip} 1)
255   dhcp_range_end=$(increment_ip ${dhcp_range_start} 8)
256   echo "${dhcp_range_start},${dhcp_range_end}"
257 }
258
259 ##finds the introspection range available via interface
260 ##uses 8 IPs, after the first 10 IPs
261 ##params: interface
262 function find_introspection_range {
263   local inspect_range_start inspect_range_end interface_ip
264   interface_ip=$(find_ip $1)
265   if [ -z "$interface_ip" ]; then
266     return 1
267   fi
268   inspect_range_start=$(increment_ip ${interface_ip} 10)
269   inspect_range_end=$(increment_ip ${inspect_range_start} 8)
270   echo "${inspect_range_start},${inspect_range_end}"
271 }
272
273 ##generate the introspection range available via CIDR
274 ##uses 8 IPs, after the first 10 IPs
275 ##params: cidr
276 function generate_introspection_range {
277   local inspect_range_start inspect_range_end first_ip
278   first_ip=$(ipcalc  -nb $1 | grep HostMin: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
279   if [ -z "$first_ip" ]; then
280     return 1
281   fi
282   inspect_range_start=$(increment_ip ${first_ip} 10)
283   inspect_range_end=$(increment_ip ${inspect_range_start} 8)
284   echo "${inspect_range_start},${inspect_range_end}"
285 }
286
287 ##finds the floating ip range available via interface
288 ##uses last 20 IPs of a subnet
289 ##params: interface
290 function find_floating_ip_range {
291   local float_range_start float_range_end interface_ip subnet_mask
292   interface_ip=$(find_ip $1)
293   subnet_mask=$(find_netmask $1)
294   if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
295     return 1
296   fi
297   float_range_end=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
298   float_range_start=$(subtract_ip ${float_range_end} 19)
299   echo "${float_range_start},${float_range_end}"
300 }
301
302 ##generate the floating range available via CIDR
303 ##uses last 20 IPs of subnet
304 ##params: cidr
305 function generate_floating_ip_range {
306   local float_range_start float_range_end last_ip
307   last_ip=$(ipcalc  -nb $1 | grep HostMax: | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
308   if [ -z "$last_ip" ]; then
309     return 1
310   fi
311   float_range_start=$(subtract_ip ${last_ip} 19)
312   float_range_end=${last_ip}
313   echo "${float_range_start},${float_range_end}"
314 }
315
316 ##attach interface to OVS and set the network config correctly
317 ##params: bride to attach to, interface to attach, network type (optional)
318 ##public indicates attaching to a public interface
319 function attach_interface_to_ovs {
320   local bridge interface
321   local if_ip if_mask if_gw if_file ovs_file
322
323   if [[ -z "$1" || -z "$2" ]]; then
324     return 1
325   else
326     bridge=$1
327     interface=$2
328   fi
329
330   if ovs-vsctl list-ports ${bridge} | grep ${interface}; then
331     return 0
332   fi
333
334   if_file=/etc/sysconfig/network-scripts/ifcfg-${interface}
335   ovs_file=/etc/sysconfig/network-scripts/ifcfg-${bridge}
336
337   if [ -e "$if_file" ]; then
338     if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file})
339     if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file})
340     if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file})
341   else
342     echo "ERROR: ifcfg file missing for ${interface}"
343     return 1
344   fi
345
346   if [[ -z "$if_ip" || -z "$if_mask" ]]; then
347     echo "ERROR: IPADDR or NETMASK missing for ${interface}"
348     return 1
349   elif [[ -z "$if_gw" && "$3" == "public_network" ]]; then
350     echo "ERROR: GATEWAY missing for ${interface}, which is public"
351     return 1
352   fi
353
354   # move old config file to .orig
355   mv -f ${if_file} ${if_file}.orig
356   echo "DEVICE=${interface}
357 DEVICETYPE=ovs
358 TYPE=OVSPort
359 PEERDNS=no
360 BOOTPROTO=static
361 NM_CONTROLLED=no
362 ONBOOT=yes
363 OVS_BRIDGE=${bridge}
364 PROMISC=yes" > ${if_file}
365
366   if [ -z ${if_gw} ]; then
367   # create bridge cfg
368   echo "DEVICE=${bridge}
369 DEVICETYPE=ovs
370 IPADDR=${if_ip}
371 NETMASK=${if_mask}
372 BOOTPROTO=static
373 ONBOOT=yes
374 TYPE=OVSBridge
375 PROMISC=yes
376 PEERDNS=no" > ${ovs_file}
377
378   else
379     echo "DEVICE=${bridge}
380 DEVICETYPE=ovs
381 IPADDR=${if_ip}
382 NETMASK=${if_mask}
383 BOOTPROTO=static
384 ONBOOT=yes
385 TYPE=OVSBridge
386 PROMISC=yes
387 GATEWAY=${if_gw}
388 PEERDNS=no" > ${ovs_file}
389   fi
390
391   sudo systemctl restart network
392 }
393
394 ##detach interface from OVS and set the network config correctly
395 ##params: bridge to detach from
396 ##assumes only 1 real interface attached to OVS
397 function detach_interface_from_ovs {
398   local bridge
399   local port_output ports_no_orig
400   local net_path
401   local if_ip if_mask if_gw
402
403   net_path=/etc/sysconfig/network-scripts/
404   if [[ -z "$1" ]]; then
405     return 1
406   else
407     bridge=$1
408   fi
409
410   # if no interfaces attached then return
411   if ! ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*"; then
412     return 0
413   fi
414
415   # look for .orig ifcfg files  to use
416   port_output=$(ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*")
417   while read -r line; do
418     if [ -z "$line" ]; then
419       continue
420     elif [ -e ${net_path}/ifcfg-${line}.orig ]; then
421       mv -f ${net_path}/ifcfg-${line}.orig ${net_path}/ifcfg-${line}
422     elif [ -e ${net_path}/ifcfg-${bridge} ]; then
423       if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file})
424       if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file})
425       if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file})
426
427       if [[ -z "$if_ip" || -z "$if_mask" ]]; then
428         echo "ERROR: IPADDR or NETMASK missing for ${bridge} and no .orig file for interface ${line}"
429         return 1
430       fi
431
432       if [ -z ${if_gw} ]; then
433         # create if cfg
434         echo "DEVICE=${line}
435 IPADDR=${if_ip}
436 NETMASK=${if_mask}
437 BOOTPROTO=static
438 ONBOOT=yes
439 TYPE=Ethernet
440 NM_CONTROLLED=no
441 PEERDNS=no" > ${net_path}/ifcfg-${line}
442       else
443         echo "DEVICE=${line}
444 IPADDR=${if_ip}
445 NETMASK=${if_mask}
446 BOOTPROTO=static
447 ONBOOT=yes
448 TYPE=Ethernet
449 NM_CONTROLLED=no
450 GATEWAY=${if_gw}
451 PEERDNS=no" > ${net_path}/ifcfg-${line}
452       fi
453       break
454     else
455       echo "ERROR: Real interface ${line} attached to bridge, but no interface or ${bridge} ifcfg file exists"
456       return 1
457     fi
458
459   done <<< "$port_output"
460
461   # modify the bridge ifcfg file
462   # to remove IP params
463   sudo sed -i 's/IPADDR=.*//' ${net_path}/ifcfg-${bridge}
464   sudo sed -i 's/NETMASK=.*//' ${net_path}/ifcfg-${bridge}
465   sudo sed -i 's/GATEWAY=.*//' ${net_path}/ifcfg-${bridge}
466
467   sudo systemctl restart network
468 }