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