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