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