5cccb06bbd77943fd6efe09f5d902f11672edf3a
[apex.git] / ci / deploy.sh
1 #!/bin/bash
2 ##############################################################################
3 # Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
10
11 # Deploy script to install provisioning server for OPNFV Apex
12 # author: Dan Radez (dradez@redhat.com)
13 # author: Tim Rozet (trozet@redhat.com)
14 #
15 # Based on RDO Manager http://www.rdoproject.org
16
17 set -e
18
19 ##VARIABLES
20 if [ "$TERM" != "unknown" ]; then
21   reset=$(tput sgr0)
22   blue=$(tput setaf 4)
23   red=$(tput setaf 1)
24   green=$(tput setaf 2)
25 else
26   reset=""
27   blue=""
28   red=""
29   green=""
30 fi
31
32 vm_index=4
33 #ha_enabled="TRUE"
34 interactive="FALSE"
35 ping_site="8.8.8.8"
36 ntp_server="pool.ntp.org"
37 net_isolation_enabled="TRUE"
38 post_config="TRUE"
39 debug="FALSE"
40
41 declare -i CNT
42 declare UNDERCLOUD
43 declare -A deploy_options_array
44 declare -A NET_MAP
45
46 SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error)
47 DEPLOY_OPTIONS=""
48 RESOURCES=/var/opt/opnfv/stack
49 CONFIG=/var/opt/opnfv
50 OPNFV_NETWORK_TYPES="admin_network private_network public_network storage_network"
51 # Netmap used to map networks to OVS bridge names
52 NET_MAP['admin_network']="brbm"
53 NET_MAP['private_network']="brbm1"
54 NET_MAP['public_network']="brbm2"
55 NET_MAP['storage_network']="brbm3"
56
57 ##FUNCTIONS
58 ##translates yaml into variables
59 ##params: filename, prefix (ex. "config_")
60 ##usage: parse_yaml opnfv_ksgen_settings.yml "config_"
61 parse_yaml() {
62    local prefix=$2
63    local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
64    sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
65         -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p"  $1 |
66    awk -F$fs '{
67       indent = length($1)/2;
68       vname[indent] = $2;
69       for (i in vname) {if (i > indent) {delete vname[i]}}
70       if (length($3) > 0) {
71          vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
72          printf("%s%s%s=%s\n", "'$prefix'",vn, $2, $3);
73       }
74    }'
75 }
76
77 ##checks if prefix exists in string
78 ##params: string, prefix
79 ##usage: contains_prefix "deploy_setting_launcher=1" "deploy_setting"
80 contains_prefix() {
81   local mystr=$1
82   local prefix=$2
83   if echo $mystr | grep -E "^$prefix.*$" > /dev/null; then
84     return 0
85   else
86     return 1
87   fi
88 }
89 ##parses variable from a string with '='
90 ##and removes global prefix
91 ##params: string, prefix
92 ##usage: parse_setting_var 'deploy_myvar=2' 'deploy_'
93 parse_setting_var() {
94   local mystr=$1
95   local prefix=$2
96   if echo $mystr | grep -E "^.+\=" > /dev/null; then
97     echo $(echo $mystr | grep -Eo "^.+\=" | tr -d '=' |  sed 's/^'"$prefix"'//')
98   else
99     return 1
100   fi
101 }
102 ##parses value from a string with '='
103 ##params: string
104 ##usage: parse_setting_value
105 parse_setting_value() {
106   local mystr=$1
107   echo $(echo $mystr | grep -Eo "\=.*$" | tr -d '=')
108 }
109 ##parses network settings yaml into globals
110 parse_network_settings() {
111   local required_network_settings="cidr"
112   local common_optional_network_settings="usable_ip_range"
113   local admin_network_optional_settings="provisioner_ip dhcp_range introspection_range"
114   local public_network_optional_settings="floating_ip_range gateway provisioner_ip"
115   local nic_value cidr
116
117   eval $(parse_yaml ${NETSETS})
118   for network in ${OPNFV_NETWORK_TYPES}; do
119     if [[ $(eval echo \${${network}_enabled}) == 'true' ]]; then
120       enabled_network_list+="${network} "
121     elif [ "${network}" == 'admin_network' ]; then
122       echo -e "${red}ERROR: You must enable admin_network and configure it explicitly or use auto-detection${reset}"
123       exit 1
124     elif [ "${network}" == 'public_network' ]; then
125       echo -e "${red}ERROR: You must enable public_network and configure it explicitly or use auto-detection${reset}"
126       exit 1
127     else
128       echo -e "${blue}INFO: Network: ${network} is disabled, will collapse into admin_network"
129     fi
130   done
131
132   # check for enabled network values
133   for enabled_network in ${enabled_network_list}; do
134     # detect required settings first to continue
135     echo -e "${blue}INFO: Detecting Required settings for: ${enabled_network}${reset}"
136     for setting in ${required_network_settings}; do
137       eval "setting_value=\${${enabled_network}_${setting}}"
138       if [ -z "${setting_value}" ]; then
139         # if setting is missing we try to autodetect
140         eval "nic_value=\${${enabled_network}_bridged_interface}"
141         if [ -n "$nic_value" ]; then
142           setting_value=$(eval find_${setting} ${nic_value})
143           if [ -n "$setting_value" ]; then
144             eval "${enabled_network}_${setting}=${setting_value}"
145             echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
146           else
147             echo -e "${red}ERROR: Auto-detection failed: ${setting} not found using interface: ${nic_value}${reset}"
148             exit 1
149           fi
150         else
151           echo -e "${red}ERROR: Required setting: ${setting} not found, and bridge interface not provided\
152 for Auto-detection${reset}"
153           exit 1
154         fi
155       else
156         echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
157       fi
158     done
159     echo -e "${blue}INFO: Detecting Common settings for: ${enabled_network}${reset}"
160     # detect optional common settings
161     # these settings can be auto-generated if missing
162     for setting in ${common_optional_network_settings}; do
163       eval "setting_value=\${${enabled_network}_${setting}}"
164       if [ -z "${setting_value}" ]; then
165         if [ -n "$nic_value" ]; then
166           setting_value=$(eval find_${setting} ${nic_value})
167         else
168           setting_value=''
169           echo -e "${blue}INFO: Skipping Auto-detection, NIC not specified for ${enabled_network}.  Attempting Auto-generation...${reset}"
170         fi
171         if [ -n "$setting_value" ]; then
172           eval "${enabled_network}_${setting}=${setting_value}"
173           echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
174         else
175           # if Auto-detection fails we can auto-generate with CIDR
176           eval "cidr=\${${enabled_network}_cidr}"
177           if [ -n "$cidr" ]; then
178             echo -e "${blue}INFO: Auto-generating: ${setting}${reset}"
179             setting_value=$(eval generate_${setting} ${cidr})
180           else
181             setting_value=''
182             echo -e "${red}ERROR: Auto-generation failed: required parameter CIDR missing for network ${enabled_network}${reset}"
183           fi
184           if [ -n "$setting_value" ]; then
185             eval "${enabled_network}_${setting}=${setting_value}"
186             echo -e "${blue}INFO: Auto-generated: ${enabled_network}_${setting}: ${setting_value}${reset}"
187           else
188             echo -e "${red}ERROR: Auto-generation failed: ${setting} not found${reset}"
189             exit 1
190           fi
191         fi
192       else
193         echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
194       fi
195     done
196     echo -e "${blue}INFO: Detecting Network Specific settings for: ${enabled_network}${reset}"
197     # detect network specific settings
198     if [ -n $(eval echo \${${network}_optional_settings}) ]; then
199       eval "network_specific_settings=\${${enabled_network}_optional_settings}"
200       for setting in ${network_specific_settings}; do
201         eval "setting_value=\${${enabled_network}_${setting}}"
202         if [ -z "${setting_value}" ]; then
203           if [ -n "$nic_value" ]; then
204             setting_value=$(eval find_${setting} ${nic_value})
205           else
206             setting_value=''
207             echo -e "${blue}INFO: Skipping Auto-detection, NIC not specified for ${enabled_network}.  Attempting Auto-generation...${reset}"
208           fi
209           if [ -n "$setting_value" ]; then
210             eval "${enabled_network}_${setting}=${setting_value}"
211             echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
212           else
213             eval "cidr=\${${enabled_network}_cidr}"
214             if [ -n "$cidr" ]; then
215               setting_value=$(eval generate_${setting} ${cidr})
216             else
217               setting_value=''
218               echo -e "${red}ERROR: Auto-generation failed: required parameter CIDR missing for network ${enabled_network}${reset}"
219             fi
220             if [ -n "$setting_value" ]; then
221               eval "${enabled_network}_${setting}=${setting_value}"
222               echo -e "${blue}INFO: Auto-generated: ${enabled_network}_${setting}: ${setting_value}${reset}"
223             else
224               echo -e "${red}ERROR: Auto-generation failed: ${setting} not found${reset}"
225               exit 1
226             fi
227           fi
228         else
229           echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
230         fi
231       done
232     fi
233   done
234 }
235 ##parses deploy settings yaml into globals and options array
236 ##params: none
237 ##usage:  parse_deploy_settings
238 parse_deploy_settings() {
239   local global_prefix="deploy_global_params_"
240   local options_prefix="deploy_deploy_options_"
241   local myvar myvalue
242   local settings=$(parse_yaml $DEPLOY_SETTINGS_FILE "deploy_")
243
244   for this_setting in $settings; do
245     if contains_prefix $this_setting $global_prefix; then
246       myvar=$(parse_setting_var $this_setting $global_prefix)
247       if [ -z "$myvar" ]; then
248         echo -e "${red}ERROR: while parsing ${DEPLOY_SETTINGS_FILE} for setting: ${this_setting}${reset}"
249       fi
250       myvalue=$(parse_setting_value $this_setting)
251       # Do not override variables set by cmdline
252       if [ -z "$(eval echo \$$myvar)" ]; then
253         eval "$myvar=\$myvalue"
254         echo -e "${blue}Global parameter set: ${myvar}:${myvalue}${reset}"
255       else
256         echo -e "${blue}Global parameter already set: ${myvar}${reset}"
257       fi
258     elif contains_prefix $this_setting $options_prefix; then
259       myvar=$(parse_setting_var $this_setting $options_prefix)
260       if [ -z "$myvar" ]; then
261         echo -e "${red}ERROR: while parsing ${DEPLOY_SETTINGS_FILE} for setting: ${this_setting}${reset}"
262       fi
263       myvalue=$(parse_setting_value $this_setting)
264       deploy_options_array[$myvar]=$myvalue
265       echo -e "${blue}Deploy option set: ${myvar}:${myvalue}${reset}"
266     fi
267   done
268 }
269 ##parses baremetal yaml settings into compatible json
270 ##writes the json to $CONFIG/instackenv_tmp.json
271 ##params: none
272 ##usage: parse_inventory_file
273 parse_inventory_file() {
274   local inventory=$(parse_yaml $INVENTORY_FILE)
275   local node_list
276   local node_prefix="node"
277   local node_count=0
278   local node_total
279   local inventory_list
280
281   # detect number of nodes
282   for entry in $inventory; do
283     if echo $entry | grep -Eo "^nodes_node[0-9]+_" > /dev/null; then
284       this_node=$(echo $entry | grep -Eo "^nodes_node[0-9]+_")
285       if [[ $inventory_list != *"$this_node"* ]]; then
286         inventory_list+="$this_node "
287       fi
288     fi
289   done
290
291   inventory_list=$(echo $inventory_list | sed 's/ $//')
292
293   for node in $inventory_list; do
294     ((node_count+=1))
295   done
296
297   node_total=$node_count
298
299   if [[ "$node_total" -lt 5 && ( ha_enabled == "TRUE" || "$ha_enabled" == "true" ) ]]; then
300     echo -e "${red}ERROR: You must provide at least 5 nodes for HA baremetal deployment${reset}"
301     exit 1
302   elif [[ "$node_total" -lt 2 ]]; then
303     echo -e "${red}ERROR: You must provide at least 2 nodes for non-HA baremetal deployment${reset}"
304     exit 1
305   fi
306
307   eval $(parse_yaml $INVENTORY_FILE)
308
309   instack_env_output="
310 {
311  \"nodes\" : [
312
313 "
314   node_count=0
315   for node in $inventory_list; do
316     ((node_count+=1))
317     node_output="
318         {
319           \"pm_password\": \"$(eval echo \${${node}ipmi_pass})\",
320           \"pm_type\": \"pxe_ipmitool\",
321           \"mac\": [
322             \"$(eval echo \${${node}mac_address})\"
323           ],
324           \"cpu\": \"$(eval echo \${${node}cpus})\",
325           \"memory\": \"$(eval echo \${${node}memory})\",
326           \"disk\": \"$(eval echo \${${node}disk})\",
327           \"arch\": \"$(eval echo \${${node}arch})\",
328           \"pm_user\": \"$(eval echo \${${node}ipmi_user})\",
329           \"pm_addr\": \"$(eval echo \${${node}ipmi_ip})\",
330           \"capabilities\": \"$(eval echo \${${node}capabilities})\"
331 "
332     instack_env_output+=${node_output}
333     if [ $node_count -lt $node_total ]; then
334       instack_env_output+="        },"
335     else
336       instack_env_output+="        }"
337     fi
338   done
339
340   instack_env_output+='
341   ]
342 }
343 '
344   #Copy instackenv.json to undercloud for baremetal
345   echo -e "{blue}Parsed instackenv JSON:\n${instack_env_output}${reset}"
346   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
347 cat > instackenv.json << EOF
348 $instack_env_output
349 EOF
350 EOI
351
352 }
353 ##verify internet connectivity
354 #params: none
355 function verify_internet {
356   if ping -c 2 $ping_site > /dev/null; then
357     if ping -c 2 www.google.com > /dev/null; then
358       echo "${blue}Internet connectivity detected${reset}"
359       return 0
360     else
361       echo "${red}Internet connectivity detected, but DNS lookup failed${reset}"
362       return 1
363     fi
364   else
365     echo "${red}No internet connectivity detected${reset}"
366     return 1
367   fi
368 }
369
370 ##download dependencies if missing and configure host
371 #params: none
372 function configure_deps {
373   if ! verify_internet; then
374     echo "${red}Will not download dependencies${reset}"
375     internet=false
376   fi
377
378   # verify ip forwarding
379   if sysctl net.ipv4.ip_forward | grep 0; then
380     sudo sysctl -w net.ipv4.ip_forward=1
381     sudo sh -c "echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf"
382   fi
383
384   # ensure no dhcp server is running on jumphost
385   if ! sudo systemctl status dhcpd | grep dead; then
386     echo "${red}WARN: DHCP Server detected on jumphost, disabling...${reset}"
387     sudo systemctl stop dhcpd
388     sudo systemctl disable dhcpd
389   fi
390
391   # ensure networks are configured
392   systemctl start openvswitch
393
394   # If flat we only use admin network
395   if [[ "$net_isolation_enabled" == "FALSE" ]]; then
396     virsh_enabled_networks="admin_network"
397   # For baremetal we only need to create/attach instack to admin and public
398   elif [ "$virtual" == "FALSE" ]; then
399     virsh_enabled_networks="admin_network public_network"
400   else
401     virsh_enabled_networks=$enabled_network_list
402   fi
403
404   for network in ${OPNFV_NETWORK_TYPES}; do
405     ovs-vsctl list-br | grep ${NET_MAP[$network]} > /dev/null || ovs-vsctl add-br ${NET_MAP[$network]}
406     virsh net-list --all | grep ${NET_MAP[$network]} > /dev/null || virsh net-create $CONFIG/${NET_MAP[$network]}-net.xml
407     virsh net-list | grep -E "${NET_MAP[$network]}\s+active" > /dev/null || virsh net-start ${NET_MAP[$network]}
408   done
409
410   echo -e "${blue}INFO: Bridges set: ${reset}"
411   ovs-vsctl list-br
412   echo -e "${blue}INFO: virsh networks set: ${reset}"
413   virsh net-list
414
415   if [[ -z "$virtual" || "$virtual" == "FALSE" ]]; then
416     # bridge interfaces to correct OVS instances for baremetal deployment
417     for network in ${enabled_network_list}; do
418       if [[ "$network" != "admin_network" && "$network" != "public_network" ]]; then
419         continue
420       fi
421       this_interface=$(eval echo \${${network}_bridged_interface})
422       # check if this a bridged interface for this network
423       if [[ ! -z "$this_interface" || "$this_interface" != "none" ]]; then
424         if ! attach_interface_to_ovs ${NET_MAP[$network]} ${this_interface} ${network}; then
425           echo -e "${red}ERROR: Unable to bridge interface ${this_interface} to bridge ${NET_MAP[$network]} for enabled network: ${network}${reset}"
426           exit 1
427         else
428           echo -e "${blue}INFO: Interface ${this_interface} bridged to bridge ${NET_MAP[$network]} for enabled network: ${network}${reset}"
429         fi
430       else
431         echo "${red}ERROR: Unable to determine interface to bridge to for enabled network: ${network}${reset}"
432         exit 1
433       fi
434     done
435   fi
436
437   # ensure storage pool exists and is started
438   virsh pool-list --all | grep default > /dev/null || virsh pool-create $CONFIG/default-pool.xml
439   virsh pool-list | grep -Eo "default\s+active" > /dev/null || virsh pool-start default
440
441   if virsh net-list | grep default > /dev/null; then
442     num_ints_same_subnet=$(ip addr show | grep "inet 192.168.122" | wc -l)
443     if [ "$num_ints_same_subnet" -gt 1 ]; then
444       virsh net-destroy default
445       ##go edit /etc/libvirt/qemu/networks/default.xml
446       sed -i 's/192.168.122/192.168.123/g' /etc/libvirt/qemu/networks/default.xml
447       sed -i 's/192.168.122/192.168.123/g' instackenv-virt.json
448       sleep 5
449       virsh net-start default
450       virsh net-autostart default
451     fi
452   fi
453
454   if ! egrep '^flags.*(vmx|svm)' /proc/cpuinfo > /dev/null; then
455     echo "${red}virtualization extensions not found, kvm kernel module insertion may fail.\n  \
456 Are you sure you have enabled vmx in your bios or hypervisor?${reset}"
457   fi
458
459   if ! lsmod | grep kvm > /dev/null; then modprobe kvm; fi
460   if ! lsmod | grep kvm_intel > /dev/null; then modprobe kvm_intel; fi
461
462   if ! lsmod | grep kvm > /dev/null; then
463     echo "${red}kvm kernel modules not loaded!${reset}"
464     return 1
465   fi
466
467   ##sshkeygen for root
468   if [ ! -e ~/.ssh/id_rsa.pub ]; then
469     ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
470   fi
471
472   echo "${blue}All dependencies installed and running${reset}"
473 }
474
475 ##verify vm exists, an has a dhcp lease assigned to it
476 ##params: none
477 function setup_instack_vm {
478   if ! virsh list --all | grep instack > /dev/null; then
479       #virsh vol-create default instack.qcow2.xml
480       virsh define $CONFIG/instack.xml
481
482       #Upload instack image
483       #virsh vol-create default --file instack.qcow2.xml
484       virsh vol-create-as default instack.qcow2 30G --format qcow2
485
486       ### this doesn't work for some reason I was getting hangup events so using cp instead
487       #virsh vol-upload --pool default --vol instack.qcow2 --file $CONFIG/stack/instack.qcow2
488       #2015-12-05 12:57:20.569+0000: 8755: info : libvirt version: 1.2.8, package: 16.el7_1.5 (CentOS BuildSystem <http://bugs.centos.org>, 2015-11-03-13:56:46, worker1.bsys.centos.org)
489       #2015-12-05 12:57:20.569+0000: 8755: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds
490       #2015-12-05 12:57:20.569+0000: 8756: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds
491       #error: cannot close volume instack.qcow2
492       #error: internal error: received hangup / error event on socket
493       #error: Reconnected to the hypervisor
494
495       instack_dst=/var/lib/libvirt/images/instack.qcow2
496       cp -f $RESOURCES/instack.qcow2 $instack_dst
497
498       # resize instack machine
499       echo "Checking if instack needs to be resized..."
500       instack_size=$(LIBGUESTFS_BACKEND=direct virt-filesystems --long -h --all -a $instack_dst |grep device | grep -Eo "[0-9\.]+G" | sed -n 's/\([0-9][0-9]*\).*/\1/p')
501       if [ "$instack_size" -lt 30 ]; then
502         qemu-img resize /var/lib/libvirt/images/instack.qcow2 +25G
503         LIBGUESTFS_BACKEND=direct virt-resize --expand /dev/sda1 $RESOURCES/instack.qcow2 $instack_dst
504         LIBGUESTFS_BACKEND=direct virt-customize -a $instack_dst --run-command 'xfs_growfs -d /dev/sda1 || true'
505         new_size=$(LIBGUESTFS_BACKEND=direct virt-filesystems --long -h --all -a $instack_dst |grep filesystem | grep -Eo "[0-9\.]+G" | sed -n 's/\([0-9][0-9]*\).*/\1/p')
506         if [ "$new_size" -lt 30 ]; then
507           echo "Error resizing instack machine, disk size is ${new_size}"
508           exit 1
509         else
510           echo "instack successfully resized"
511         fi
512       else
513         echo "skipped instack resize, upstream is large enough"
514       fi
515
516   else
517       echo "Found Instack VM, using existing VM"
518   fi
519
520   # if the VM is not running update the authkeys and start it
521   if ! virsh list | grep instack > /dev/null; then
522     echo "Injecting ssh key to instack VM"
523     virt-customize -c qemu:///system -d instack --run-command "mkdir -p /root/.ssh/" \
524         --upload ~/.ssh/id_rsa.pub:/root/.ssh/authorized_keys \
525         --run-command "chmod 600 /root/.ssh/authorized_keys && restorecon /root/.ssh/authorized_keys" \
526         --run-command "cp /root/.ssh/authorized_keys /home/stack/.ssh/" \
527         --run-command "chown stack:stack /home/stack/.ssh/authorized_keys && chmod 600 /home/stack/.ssh/authorized_keys"
528     virsh start instack
529   fi
530
531   sleep 3 # let DHCP happen
532
533   CNT=10
534   echo -n "${blue}Waiting for instack's dhcp address${reset}"
535   while ! grep instack /var/lib/libvirt/dnsmasq/default.leases > /dev/null && [ $CNT -gt 0 ]; do
536       echo -n "."
537       sleep 3
538       CNT=CNT-1
539   done
540
541   # get the instack VM IP
542   UNDERCLOUD=$(grep instack /var/lib/libvirt/dnsmasq/default.leases | awk '{print $3}' | head -n 1)
543   if [ -z "$UNDERCLOUD" ]; then
544     #if not found then dnsmasq may be using leasefile-ro
545     instack_mac=$(virsh domiflist instack | grep default | \
546                   grep -Eo "[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+")
547     UNDERCLOUD=$(/usr/sbin/arp -e | grep ${instack_mac} | awk {'print $1'})
548
549     if [ -z "$UNDERCLOUD" ]; then
550       echo "\n\nNever got IP for Instack. Can Not Continue."
551       exit 1
552     else
553       echo -e "${blue}\rInstack VM has IP $UNDERCLOUD${reset}"
554     fi
555   else
556      echo -e "${blue}\rInstack VM has IP $UNDERCLOUD${reset}"
557   fi
558
559   CNT=10
560   echo -en "${blue}\rValidating instack VM connectivity${reset}"
561   while ! ping -c 1 $UNDERCLOUD > /dev/null && [ $CNT -gt 0 ]; do
562       echo -n "."
563       sleep 3
564       CNT=$CNT-1
565   done
566   if [ "$CNT" -eq 0 ]; then
567       echo "Failed to contact Instack. Can Not Continue"
568       exit 1
569   fi
570   CNT=10
571   while ! ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "echo ''" 2>&1> /dev/null && [ $CNT -gt 0 ]; do
572       echo -n "."
573       sleep 3
574       CNT=$CNT-1
575   done
576   if [ "$CNT" -eq 0 ]; then
577       echo "Failed to connect to Instack. Can Not Continue"
578       exit 1
579   fi
580
581   # extra space to overwrite the previous connectivity output
582   echo -e "${blue}\r                                                                 ${reset}"
583
584   #add the instack public interface if net isolation is enabled (more than just admin network)
585   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
586     virsh attach-interface --domain instack --type network --source ${NET_MAP['public_network']} --model rtl8139 --config --live
587     sleep 1
588     ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "if ! ip a s eth2 | grep ${public_network_provisioner_ip} > /dev/null; then ip a a ${public_network_provisioner_ip}/${public_network_cidr##*/} dev eth2; ip link set up dev eth2; fi"
589   fi
590   # ssh key fix for stack user
591   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "restorecon -r /home/stack"
592 }
593
594 ##Create virtual nodes in virsh
595 ##params: none
596 function setup_virtual_baremetal {
597   for i in $(seq 0 $vm_index); do
598     if ! virsh list --all | grep baremetalbrbm_brbm1_brbm2_brbm3_${i} > /dev/null; then
599       if [ ! -e $CONFIG/baremetalbrbm_brbm1_brbm2_brbm3_${i}.xml ]; then
600         define_virtual_node baremetalbrbm_brbm1_brbm2_brbm3_${i}
601       fi
602       # Fix for ramdisk using wrong pxeboot interface
603       # TODO: revisit this and see if there's a more proper fix
604       sed -i "/^\s*<source network='brbm2'\/>/{
605         N
606         s/^\(.*\)virtio\(.*\)$/\1rtl8139\2/
607         }" $CONFIG/baremetalbrbm_brbm1_brbm2_brbm3_${i}.xml
608       virsh define $CONFIG/baremetalbrbm_brbm1_brbm2_brbm3_${i}.xml
609     else
610       echo "Found Baremetal ${i} VM, using existing VM"
611     fi
612     virsh vol-list default | grep baremetalbrbm_brbm1_brbm2_brbm3_${i} 2>&1> /dev/null || virsh vol-create-as default baremetalbrbm_brbm1_brbm2_brbm3_${i}.qcow2 40G --format qcow2
613   done
614
615 }
616
617 ##Set network-environment settings
618 ##params: network-environment file to edit
619 function configure_network_environment {
620   local tht_dir nic_ext
621   tht_dir=/usr/share/openstack-tripleo-heat-templates/network
622   nic_ext=''
623
624   sed -i '/ControlPlaneSubnetCidr/c\\  ControlPlaneSubnetCidr: "'${admin_network_cidr##*/}'"' $1
625   sed -i '/ControlPlaneDefaultRoute/c\\  ControlPlaneDefaultRoute: '${admin_network_provisioner_ip}'' $1
626   sed -i '/ExternalNetCidr/c\\  ExternalNetCidr: '${public_network_cidr}'' $1
627   sed -i "/ExternalAllocationPools/c\\  ExternalAllocationPools: [{'start': '${public_network_usable_ip_range%%,*}', 'end': '${public_network_usable_ip_range##*,}'}]" $1
628   sed -i '/ExternalInterfaceDefaultRoute/c\\  ExternalInterfaceDefaultRoute: '${public_network_gateway}'' $1
629   sed -i '/EC2MetadataIp/c\\  EC2MetadataIp: '${admin_network_provisioner_ip}'' $1
630
631   # check for private network
632   if [[ ! -z "$private_network_enabled" && "$private_network_enabled" == "true" ]]; then
633       sed -i 's#^.*Network::Tenant.*$#  OS::TripleO::Network::Tenant: '${tht_dir}'/tenant.yaml#' $1
634       sed -i 's#^.*Controller::Ports::TenantPort:.*$#  OS::TripleO::Controller::Ports::TenantPort: '${tht_dir}'/ports/tenant.yaml#' $1
635       sed -i 's#^.*Compute::Ports::TenantPort:.*$#  OS::TripleO::Compute::Ports::TenantPort: '${tht_dir}'/ports/tenant.yaml#' $1
636       sed -i "/TenantAllocationPools/c\\  TenantAllocationPools: [{'start': '${private_network_usable_ip_range%%,*}', 'end': '${private_network_usable_ip_range##*,}'}]" $1
637       sed -i '/TenantNetCidr/c\\  TenantNetCidr: '${private_network_cidr}'' $1
638       nic_ext+=_private
639   else
640       sed -i 's#^.*Network::Tenant.*$#  OS::TripleO::Network::Tenant: '${tht_dir}'/noop.yaml#' $1
641       sed -i 's#^.*Controller::Ports::TenantPort:.*$#  OS::TripleO::Controller::Ports::TenantPort: '${tht_dir}'/ports/noop.yaml#' $1
642       sed -i 's#^.*Compute::Ports::TenantPort:.*$#  OS::TripleO::Compute::Ports::TenantPort: '${tht_dir}'/ports/noop.yaml#' $1
643   fi
644
645   # check for storage network
646   if [[ ! -z "$storage_network_enabled" && "$storage_network_enabled" == "true" ]]; then
647       sed -i 's#^.*Network::Storage.*$#  OS::TripleO::Network::Storage: '${tht_dir}'/storage.yaml#' $1
648       sed -i 's#^.*Controller::Ports::StoragePort:.*$#  OS::TripleO::Controller::Ports::StoragePort: '${tht_dir}'/ports/storage.yaml#' $1
649       sed -i 's#^.*Compute::Ports::StoragePort:.*$#  OS::TripleO::Compute::Ports::StoragePort: '${tht_dir}'/ports/storage.yaml#' $1
650       sed -i "/StorageAllocationPools/c\\  StorageAllocationPools: [{'start': '${storage_network_usable_ip_range%%,*}', 'end': '${storage_network_usable_ip_range##*,}'}]" $1
651       sed -i '/StorageNetCidr/c\\  StorageNetCidr: '${storage_network_cidr}'' $1
652       nic_ext+=_storage
653   else
654       sed -i 's#^.*Network::Storage.*$#  OS::TripleO::Network::Storage: '${tht_dir}'/noop.yaml#' $1
655       sed -i 's#^.*Controller::Ports::StoragePort:.*$#  OS::TripleO::Controller::Ports::StoragePort: '${tht_dir}'/ports/noop.yaml#' $1
656       sed -i 's#^.*Compute::Ports::StoragePort:.*$#  OS::TripleO::Compute::Ports::StoragePort: '${tht_dir}'/ports/noop.yaml#' $1
657   fi
658
659   sed -i 's#^.*Controller::Net::SoftwareConfig:.*$#  OS::TripleO::Controller::Net::SoftwareConfig: nics/controller'${nic_ext}'.yaml#' $1
660
661   # check for ODL L3
662   if [ ${deploy_options_array['sdn_l3']} == 'true' ]; then
663       nic_ext+=_br-ex
664   fi
665
666   if [ ${deploy_options_array['sdn_controller']} == 'onos' ]; then
667       nic_ext+=_no-public-ip
668   fi
669
670   # set nics appropriately
671   sed -i 's#^.*Compute::Net::SoftwareConfig:.*$#  OS::TripleO::Compute::Net::SoftwareConfig: nics/compute'${nic_ext}'.yaml#' $1
672
673 }
674 ##Copy over the glance images and instack json file
675 ##params: none
676 function configure_undercloud {
677
678   echo
679   echo "Copying configuration files to instack"
680   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
681     configure_network_environment $CONFIG/network-environment.yaml
682     echo -e "${blue}Network Environment set for Deployment: ${reset}"
683     cat $CONFIG/network-environment.yaml
684     scp ${SSH_OPTIONS[@]} $CONFIG/network-environment.yaml "stack@$UNDERCLOUD":
685   fi
686   scp ${SSH_OPTIONS[@]} -r $CONFIG/nics/ "stack@$UNDERCLOUD":
687
688   # ensure stack user on instack machine has an ssh key
689   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "if [ ! -e ~/.ssh/id_rsa.pub ]; then ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa; fi"
690
691   if [ "$virtual" == "TRUE" ]; then
692
693       # copy the instack vm's stack user's pub key to
694       # root's auth keys so that instack can control
695       # vm power on the hypervisor
696       ssh ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "cat /home/stack/.ssh/id_rsa.pub" >> /root/.ssh/authorized_keys
697
698       # fix MACs to match new setup
699       for i in $(seq 0 $vm_index); do
700         pyscript="import json
701 data = json.load(open('$CONFIG/instackenv-virt.json'))
702 print data['nodes'][$i]['mac'][0]"
703
704         old_mac=$(python -c "$pyscript")
705         new_mac=$(virsh dumpxml baremetalbrbm_brbm1_brbm2_brbm3_$i | grep "mac address" | cut -d = -f2 | grep -Eo "[0-9a-f:]+")
706         # this doesn't work with multiple vnics on the vms
707         #if [ "$old_mac" != "$new_mac" ]; then
708         #  echo "${blue}Modifying MAC for node from $old_mac to ${new_mac}${reset}"
709         #  sed -i 's/'"$old_mac"'/'"$new_mac"'/' $CONFIG/instackenv-virt.json
710         #fi
711       done
712
713       DEPLOY_OPTIONS+=" --libvirt-type qemu"
714       INSTACKENV=$CONFIG/instackenv-virt.json
715
716       # upload instackenv file to Instack for virtual deployment
717       scp ${SSH_OPTIONS[@]} $INSTACKENV "stack@$UNDERCLOUD":instackenv.json
718   fi
719
720   # allow stack to control power management on the hypervisor via sshkey
721   # only if this is a virtual deployment
722   if [ "$virtual" == "TRUE" ]; then
723       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
724 while read -r line; do
725   stack_key=\${stack_key}\\\\\\\\n\${line}
726 done < <(cat ~/.ssh/id_rsa)
727 stack_key=\$(echo \$stack_key | sed 's/\\\\\\\\n//')
728 sed -i 's~INSERT_STACK_USER_PRIV_KEY~'"\$stack_key"'~' instackenv.json
729 EOI
730   fi
731
732   # copy stack's ssh key to this users authorized keys
733   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "cat /home/stack/.ssh/id_rsa.pub" >> ~/.ssh/authorized_keys
734
735   # disable requiretty for sudo
736   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "sed -i 's/Defaults\s*requiretty//'" /etc/sudoers
737
738   # configure undercloud on Undercloud VM
739   echo "Running undercloud configuration."
740   echo "Logging undercloud configuration to instack:/home/stack/apex-undercloud-install.log"
741   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI
742 if [[ "$net_isolation_enabled" == "TRUE" ]]; then
743   sed -i 's/#local_ip/local_ip/' undercloud.conf
744   sed -i 's/#network_gateway/network_gateway/' undercloud.conf
745   sed -i 's/#network_cidr/network_cidr/' undercloud.conf
746   sed -i 's/#dhcp_start/dhcp_start/' undercloud.conf
747   sed -i 's/#dhcp_end/dhcp_end/' undercloud.conf
748   sed -i 's/#inspection_iprange/inspection_iprange/' undercloud.conf
749   sed -i 's/#undercloud_debug/undercloud_debug/' undercloud.conf
750
751   openstack-config --set undercloud.conf DEFAULT local_ip ${admin_network_provisioner_ip}/${admin_network_cidr##*/}
752   openstack-config --set undercloud.conf DEFAULT network_gateway ${admin_network_provisioner_ip}
753   openstack-config --set undercloud.conf DEFAULT network_cidr ${admin_network_cidr}
754   openstack-config --set undercloud.conf DEFAULT dhcp_start ${admin_network_dhcp_range%%,*}
755   openstack-config --set undercloud.conf DEFAULT dhcp_end ${admin_network_dhcp_range##*,}
756   openstack-config --set undercloud.conf DEFAULT inspection_iprange ${admin_network_introspection_range}
757   openstack-config --set undercloud.conf DEFAULT undercloud_debug false
758
759 fi
760
761 sudo sed -i '/CephClusterFSID:/c\\  CephClusterFSID: \\x27$(cat /proc/sys/kernel/random/uuid)\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml
762 sudo sed -i '/CephMonKey:/c\\  CephMonKey: \\x27'"\$(ceph-authtool --gen-print-key)"'\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml
763 sudo sed -i '/CephAdminKey:/c\\  CephAdminKey: \\x27'"\$(ceph-authtool --gen-print-key)"'\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml
764
765 openstack undercloud install &> apex-undercloud-install.log
766 sleep 30
767 sudo systemctl restart openstack-glance-api
768 sudo systemctl restart openstack-nova-conductor
769 sudo systemctl restart openstack-nova-compute
770 EOI
771 # WORKAROUND: must restart the above services to fix sync problem with nova compute manager
772 # TODO: revisit and file a bug if necessary. This should eventually be removed
773 # as well as glance api problem
774 echo -e "${blue}INFO: Sleeping 15 seconds while services come back from restart${reset}"
775 sleep 15
776
777 }
778
779 ##preping it for deployment and launch the deploy
780 ##params: none
781 function undercloud_prep_overcloud_deploy {
782   if [[ ${#deploy_options_array[@]} -eq 0 || ${deploy_options_array['sdn_controller']} == 'opendaylight' ]]; then
783     if [ ${deploy_options_array['sdn_l3']} == 'true' ]; then
784       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight_l3.yaml"
785     elif [ ${deploy_options_array['sfc']} == 'true' ]; then
786       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight_sfc.yaml"
787     else
788       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight.yaml"
789     fi
790     SDN_IMAGE=opendaylight
791     if [ ${deploy_options_array['sfc']} == 'true' ]; then
792       SDN_IMAGE+=-sfc
793     fi
794   elif [ ${deploy_options_array['sdn_controller']} == 'opendaylight-external' ]; then
795     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight-external.yaml"
796     SDN_IMAGE=opendaylight
797   elif [ ${deploy_options_array['sdn_controller']} == 'onos' ]; then
798     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/onos.yaml"
799     SDN_IMAGE=opendaylight
800   elif [ ${deploy_options_array['sdn_controller']} == 'opencontrail' ]; then
801     echo -e "${red}ERROR: OpenContrail is currently unsupported...exiting${reset}"
802     exit 1
803   elif [[ -z ${deploy_options_array['sdn_controller']} || ${deploy_options_array['sdn_controller']} == 'false' ]]; then
804     echo -e "${blue}INFO: SDN Controller disabled...will deploy nosdn scenario${reset}"
805   else
806     echo "${red}Invalid sdn_controller: ${deploy_options_array['sdn_controller']}${reset}"
807     echo "${red}Valid choices are opendaylight, opendaylight-external, onos, opencontrail, false, or null${reset}"
808     exit 1
809   fi
810
811   echo "Copying overcloud image to instack"
812   scp ${SSH_OPTIONS[@]} $RESOURCES/overcloud-full-${SDN_IMAGE}.qcow2 "stack@$UNDERCLOUD":overcloud-full.qcow2
813
814   # make sure ceph is installed
815   DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml"
816
817   # scale compute nodes according to inventory
818   total_nodes=$(ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "cat /home/stack/instackenv.json | grep -c memory")
819
820   # check if HA is enabled
821   if [[ "$ha_enabled" == "TRUE" || "$ha_enabled" == "true" ]]; then
822      DEPLOY_OPTIONS+=" --control-scale 3"
823      compute_nodes=$((total_nodes - 3))
824      DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/puppet-pacemaker.yaml"
825   else
826      compute_nodes=$((total_nodes - 1))
827   fi
828
829   if [ "$compute_nodes" -le 0 ]; then
830     echo -e "${red}ERROR: Invalid number of compute nodes: ${compute_nodes}. Check your inventory file.${reset}"
831     exit 1
832   else
833     echo -e "${blue}INFO: Number of compute nodes set for deployment: ${compute_nodes}${reset}"
834     DEPLOY_OPTIONS+=" --compute-scale ${compute_nodes}"
835   fi
836
837   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
838      #DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/network-isolation.yaml"
839      DEPLOY_OPTIONS+=" -e network-environment.yaml"
840   fi
841
842   if [[ "$ha_enabled" == "TRUE" || "$ha_enabled" == "true"  ]] || [[ "$net_isolation_enabled" == "TRUE" ]]; then
843      DEPLOY_OPTIONS+=" --ntp-server $ntp_server"
844   fi
845
846   if [[ ! "$virtual" == "TRUE" ]]; then
847      DEPLOY_OPTIONS+=" --control-flavor control --compute-flavor compute"
848   fi
849
850   echo -e "${blue}INFO: Deploy options set:\n${DEPLOY_OPTIONS}${reset}"
851
852   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
853 source stackrc
854 set -o errexit
855 echo "Uploading overcloud glance images"
856 openstack overcloud image upload
857 echo "Configuring undercloud and discovering nodes"
858 openstack baremetal import --json instackenv.json
859 openstack baremetal configure boot
860 #if [[ -z "$virtual" ]]; then
861 #  openstack baremetal introspection bulk start
862 #fi
863 echo "Configuring flavors"
864 for flavor in baremetal control compute; do
865   echo -e "${blue}INFO: Updating flavor: \${flavor}${reset}"
866   if openstack flavor list | grep \${flavor}; then
867     openstack flavor delete \${flavor}
868   fi
869   openstack flavor create --id auto --ram 4096 --disk 39 --vcpus 1 \${flavor}
870   if ! openstack flavor list | grep \${flavor}; then
871     echo -e "${red}ERROR: Unable to create flavor \${flavor}${reset}"
872   fi
873 done
874 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" baremetal
875 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="control" control
876 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="compute" compute
877 echo "Configuring nameserver on ctlplane network"
878 neutron subnet-update \$(neutron subnet-list | grep -v id | grep -v \\\\-\\\\- | awk {'print \$2'}) --dns-nameserver 8.8.8.8
879 echo "Executing overcloud deployment, this should run for an extended period without output."
880 sleep 60 #wait for Hypervisor stats to check-in to nova
881 # save deploy command so it can be used for debugging
882 cat > deploy_command << EOF
883 openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90
884 EOF
885 EOI
886
887   if [ "$interactive" == "TRUE" ]; then
888     if ! prompt_user "Overcloud Deployment"; then
889       echo -e "${blue}INFO: User requests exit${reset}"
890       exit 0
891     fi
892   fi
893
894   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
895 source stackrc
896 set -o errexit
897 openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90
898 EOI
899
900   if [ "$debug" == 'TRUE' ]; then
901       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
902 source overcloudrc
903 echo "Keystone Endpoint List:"
904 keystone endpoint-list
905 echo "Keystone Service List"
906 keystone service-list
907 cinder quota-show \$(openstack project list | grep admin | awk {'print \$2'})
908 EOI
909   fi
910 }
911
912 ##Post configuration after install
913 ##params: none
914 function configure_post_install {
915   local opnfv_attach_networks ovs_ip ip_range net_cidr tmp_ip
916   opnfv_attach_networks="admin_network public_network"
917
918   echo -e "${blue}INFO: Post Install Configuration Running...${reset}"
919
920   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
921 source overcloudrc
922 set -o errexit
923 echo "Configuring Neutron external network"
924 neutron net-create external --router:external=True
925 neutron subnet-create --name external-net --disable-dhcp external --gateway ${public_network_gateway} --allocation-pool start=${public_network_floating_ip_range%%,*},end=${public_network_floating_ip_range##*,} ${public_network_cidr}
926 EOI
927
928   echo -e "${blue}INFO: Checking if OVS bridges have IP addresses...${reset}"
929   for network in ${opnfv_attach_networks}; do
930     ovs_ip=$(find_ip ${NET_MAP[$network]})
931     tmp_ip=''
932     if [ -n "$ovs_ip" ]; then
933       echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} has IP address ${ovs_ip}${reset}"
934     else
935       echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} missing IP, will configure${reset}"
936       # use last IP of allocation pool
937       eval "ip_range=\${${network}_usable_ip_range}"
938       ovs_ip=${ip_range##*,}
939       eval "net_cidr=\${${network}_cidr}"
940       sudo ip addr add ${ovs_ip}/${net_cidr##*/} dev ${NET_MAP[$network]}
941       sudo ip link set up ${NET_MAP[$network]}
942       tmp_ip=$(find_ip ${NET_MAP[$network]})
943       if [ -n "$tmp_ip" ]; then
944         echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} IP set: ${tmp_ip}${reset}"
945         continue
946       else
947         echo -e "${red}ERROR: Unable to set OVS Bridge ${NET_MAP[$network]} with IP: ${ovs_ip}${reset}"
948         return 1
949       fi
950     fi
951   done
952
953   # for virtual, we NAT public network through instack
954   if [ "$virtual" == "TRUE" ]; then
955     if ! configure_undercloud_nat ${public_network_cidr}; then
956       echo -e "${red}ERROR: Unable to NAT undercloud with external net: ${public_network_cidr}${reset}"
957       exit 1
958     else
959       echo -e "${blue}INFO: Undercloud (instack VM) has been setup to NAT Overcloud public network${reset}"
960     fi
961   fi
962
963   # for sfc deployments we need the vxlan workaround
964   if [ ${deploy_options_array['sfc']} == 'true' ]; then
965       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
966 source stackrc
967 set -o errexit
968 for node in \$(nova list | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"); do
969 ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
970 sudo ifconfig br-int up
971 sudo ip route add 123.123.123.0/24 dev br-int
972 EOF
973 done
974 EOI
975   fi
976 }
977
978 display_usage() {
979   echo -e "Usage:\n$0 [arguments] \n"
980   echo -e "   -c|--config : Directory to configuration files. Optional.  Defaults to /var/opt/opnfv/ \n"
981   echo -e "   -d|--deploy-settings : Full path to deploy settings yaml file. Optional.  Defaults to null \n"
982   echo -e "   -i|--inventory : Full path to inventory yaml file. Required only for baremetal \n"
983   echo -e "   -n|--net-settings : Full path to network settings file. Optional. \n"
984   echo -e "   -p|--ping-site : site to use to verify IP connectivity. Optional. Defaults to 8.8.8.8 \n"
985   echo -e "   -r|--resources : Directory to deployment resources. Optional.  Defaults to /var/opt/opnfv/stack \n"
986   echo -e "   -v|--virtual : Virtualize overcloud nodes instead of using baremetal. \n"
987   echo -e "   --no-ha : disable High Availability deployment scheme, this assumes a single controller and single compute node \n"
988   echo -e "   --flat : disable Network Isolation and use a single flat network for the underlay network.\n"
989   echo -e "   --no-post-config : disable Post Install configuration."
990   echo -e "   --debug : enable debug output."
991   echo -e "   --interactive : enable interactive deployment mode which requires user to confirm steps of deployment."
992 }
993
994 ##translates the command line parameters into variables
995 ##params: $@ the entire command line is passed
996 ##usage: parse_cmd_line() "$@"
997 parse_cmdline() {
998   echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n"
999   echo "Use -h to display help"
1000   sleep 2
1001
1002   while [ "${1:0:1}" = "-" ]
1003   do
1004     case "$1" in
1005         -h|--help)
1006                 display_usage
1007                 exit 0
1008             ;;
1009         -c|--config)
1010                 CONFIG=$2
1011                 echo "Deployment Configuration Directory Overridden to: $2"
1012                 shift 2
1013             ;;
1014         -d|--deploy-settings)
1015                 DEPLOY_SETTINGS_FILE=$2
1016                 echo "Deployment Configuration file: $2"
1017                 shift 2
1018             ;;
1019         -i|--inventory)
1020                 INVENTORY_FILE=$2
1021                 shift 2
1022             ;;
1023         -n|--net-settings)
1024                 NETSETS=$2
1025                 echo "Network Settings Configuration file: $2"
1026                 shift 2
1027             ;;
1028         -p|--ping-site)
1029                 ping_site=$2
1030                 echo "Using $2 as the ping site"
1031                 shift 2
1032             ;;
1033         -r|--resources)
1034                 RESOURCES=$2
1035                 echo "Deployment Resources Directory Overridden to: $2"
1036                 shift 2
1037             ;;
1038         -v|--virtual)
1039                 virtual="TRUE"
1040                 echo "Executing a Virtual Deployment"
1041                 shift 1
1042             ;;
1043         --no-ha )
1044                 ha_enabled="FALSE"
1045                 echo "HA Deployment Disabled"
1046                 shift 1
1047             ;;
1048         --flat )
1049                 net_isolation_enabled="FALSE"
1050                 echo "Underlay Network Isolation Disabled: using flat configuration"
1051                 shift 1
1052             ;;
1053         --no-post-config )
1054                 post_config="FALSE"
1055                 echo "Post install configuration disabled"
1056                 shift 1
1057             ;;
1058         --debug )
1059                 debug="TRUE"
1060                 echo "Enable debug output"
1061                 shift 1
1062             ;;
1063         --interactive )
1064                 interactive="TRUE"
1065                 echo "Interactive mode enabled"
1066                 shift 1
1067             ;;
1068         *)
1069                 display_usage
1070                 exit 1
1071             ;;
1072     esac
1073   done
1074
1075   if [[ ! -z "$NETSETS" && "$net_isolation_enabled" == "FALSE" ]]; then
1076     echo -e "${red}INFO: Single flat network requested. Ignoring any network settings!${reset}"
1077   elif [[ -z "$NETSETS" && "$net_isolation_enabled" == "TRUE" ]]; then
1078     echo -e "${red}ERROR: You must provide a network_settings file with -n or use --flat to force a single flat network${reset}"
1079     exit 1
1080   fi
1081
1082   if [[ -n "$virtual" && -n "$INVENTORY_FILE" ]]; then
1083     echo -e "${red}ERROR: You should not specify an inventory with virtual deployments${reset}"
1084     exit 1
1085   fi
1086
1087   if [[ -z "$DEPLOY_SETTINGS_FILE" || ! -f "$DEPLOY_SETTINGS_FILE" ]]; then
1088     echo -e "${red}ERROR: Deploy Settings: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
1089     exit 1
1090   fi
1091
1092   if [[ ! -z "$NETSETS" && ! -f "$NETSETS" ]]; then
1093     echo -e "${red}ERROR: Network Settings: ${NETSETS} does not exist! Exiting...${reset}"
1094     exit 1
1095   fi
1096
1097   if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
1098     echo -e "{$red}ERROR: Inventory File: ${INVENTORY_FILE} does not exist! Exiting...${reset}"
1099     exit 1
1100   fi
1101
1102   if [[ -z "$virtual" && -z "$INVENTORY_FILE" ]]; then
1103     echo -e "${red}ERROR: You must specify an inventory file for baremetal deployments! Exiting...${reset}"
1104     exit 1
1105   fi
1106
1107   if [[ "$net_isolation_enabled" == "FALSE" && "$post_config" == "TRUE" ]]; then
1108     echo -e "${blue}INFO: Post Install Configuration will be skipped.  It is not supported with --flat${reset}"
1109     post_config="FALSE"
1110   fi
1111
1112   ##LIBRARIES
1113   # Do this after cli parse so that $CONFIG is set properly
1114   source $CONFIG/lib/common-functions.sh
1115   source $CONFIG/lib/installer/onos/onos_gw_mac_update.sh
1116
1117 }
1118
1119 ##END FUNCTIONS
1120
1121 main() {
1122   parse_cmdline "$@"
1123   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
1124     echo -e "${blue}INFO: Parsing network settings file...${reset}"
1125     parse_network_settings
1126   fi
1127   if ! configure_deps; then
1128     echo -e "${red}Dependency Validation Failed, Exiting.${reset}"
1129     exit 1
1130   fi
1131   if [ -n "$DEPLOY_SETTINGS_FILE" ]; then
1132     parse_deploy_settings
1133   fi
1134   setup_instack_vm
1135   if [ "$virtual" == "TRUE" ]; then
1136     setup_virtual_baremetal
1137   elif [ -n "$INVENTORY_FILE" ]; then
1138     parse_inventory_file
1139   fi
1140   configure_undercloud
1141   undercloud_prep_overcloud_deploy
1142   if [ "$post_config" == "TRUE" ]; then
1143     if ! configure_post_install; then
1144       echo -e "${red}ERROR:Post Install Configuration Failed, Exiting.${reset}"
1145       exit 1
1146     else
1147       echo -e "${blue}INFO: Post Install Configuration Complete${reset}"
1148     fi
1149   fi
1150   if [[ ${deploy_options_array['sdn_controller']} == 'onos' ]]; then
1151     if ! onos_update_gw_mac ${public_network_cidr} ${public_network_gateway}; then
1152       echo -e "${red}ERROR:ONOS Post Install Configuration Failed, Exiting.${reset}"
1153       exit 1
1154     else
1155       echo -e "${blue}INFO: ONOS Post Install Configuration Complete${reset}"
1156     fi
1157   fi
1158 }
1159
1160 main "$@"