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