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