01bf003d49a56304ee5501e9becd6ee61850ba3c
[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 [[ -n "$this_interface" || "$this_interface" != "none" ]]; then
393         ovs-vsctl list-ports ${NET_MAP[$network]} | grep ${this_interface} || ovs-vsctl add-port ${NET_MAP[$network]} ${this_interface}
394       else
395         echo "${red}ERROR: Unable to determine interface to bridge to for enabled network: ${network}${reset}"
396         exit 1
397       fi
398     done
399   fi
400
401   # ensure storage pool exists and is started
402   virsh pool-list --all | grep default > /dev/null || virsh pool-create $CONFIG/default-pool.xml
403   virsh pool-list | grep -Eo "default\s+active" > /dev/null || virsh pool-start default
404
405   if virsh net-list | grep default > /dev/null; then
406     num_ints_same_subnet=$(ip addr show | grep "inet 192.168.122" | wc -l)
407     if [ "$num_ints_same_subnet" -gt 1 ]; then
408       virsh net-destroy default
409       ##go edit /etc/libvirt/qemu/networks/default.xml
410       sed -i 's/192.168.122/192.168.123/g' /etc/libvirt/qemu/networks/default.xml
411       sed -i 's/192.168.122/192.168.123/g' instackenv-virt.json
412       sleep 5
413       virsh net-start default
414       virsh net-autostart default
415     fi
416   fi
417
418   if ! egrep '^flags.*(vmx|svm)' /proc/cpuinfo > /dev/null; then
419     echo "${red}virtualization extensions not found, kvm kernel module insertion may fail.\n  \
420 Are you sure you have enabled vmx in your bios or hypervisor?${reset}"
421   fi
422
423   if ! lsmod | grep kvm > /dev/null; then modprobe kvm; fi
424   if ! lsmod | grep kvm_intel > /dev/null; then modprobe kvm_intel; fi
425
426   if ! lsmod | grep kvm > /dev/null; then
427     echo "${red}kvm kernel modules not loaded!${reset}"
428     return 1
429   fi
430
431   ##sshkeygen for root
432   if [ ! -e ~/.ssh/id_rsa.pub ]; then
433     ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
434   fi
435
436   echo "${blue}All dependencies installed and running${reset}"
437 }
438
439 ##verify vm exists, an has a dhcp lease assigned to it
440 ##params: none
441 function setup_instack_vm {
442   if ! virsh list --all | grep instack > /dev/null; then
443       #virsh vol-create default instack.qcow2.xml
444       virsh define $CONFIG/instack.xml
445
446       #Upload instack image
447       #virsh vol-create default --file instack.qcow2.xml
448       virsh vol-create-as default instack.qcow2 30G --format qcow2
449
450       ### this doesn't work for some reason I was getting hangup events so using cp instead
451       #virsh vol-upload --pool default --vol instack.qcow2 --file $CONFIG/stack/instack.qcow2
452       #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)
453       #2015-12-05 12:57:20.569+0000: 8755: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds
454       #2015-12-05 12:57:20.569+0000: 8756: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds
455       #error: cannot close volume instack.qcow2
456       #error: internal error: received hangup / error event on socket
457       #error: Reconnected to the hypervisor
458
459       instack_dst=/var/lib/libvirt/images/instack.qcow2
460       cp -f $RESOURCES/instack.qcow2 $instack_dst
461
462       # resize instack machine
463       echo "Checking if instack needs to be resized..."
464       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')
465       if [ "$instack_size" -lt 30 ]; then
466         qemu-img resize /var/lib/libvirt/images/instack.qcow2 +25G
467         LIBGUESTFS_BACKEND=direct virt-resize --expand /dev/sda1 $RESOURCES/instack.qcow2 $instack_dst
468         LIBGUESTFS_BACKEND=direct virt-customize -a $instack_dst --run-command 'xfs_growfs -d /dev/sda1 || true'
469         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')
470         if [ "$new_size" -lt 30 ]; then
471           echo "Error resizing instack machine, disk size is ${new_size}"
472           exit 1
473         else
474           echo "instack successfully resized"
475         fi
476       else
477         echo "skipped instack resize, upstream is large enough"
478       fi
479
480   else
481       echo "Found Instack VM, using existing VM"
482   fi
483
484   # if the VM is not running update the authkeys and start it
485   if ! virsh list | grep instack > /dev/null; then
486     echo "Injecting ssh key to instack VM"
487     virt-customize -c qemu:///system -d instack --run-command "mkdir /root/.ssh/" \
488         --upload ~/.ssh/id_rsa.pub:/root/.ssh/authorized_keys \
489         --run-command "chmod 600 /root/.ssh/authorized_keys && restorecon /root/.ssh/authorized_keys" \
490         --run-command "cp /root/.ssh/authorized_keys /home/stack/.ssh/" \
491         --run-command "chown stack:stack /home/stack/.ssh/authorized_keys && chmod 600 /home/stack/.ssh/authorized_keys"
492     virsh start instack
493   fi
494
495   sleep 3 # let DHCP happen
496
497   CNT=10
498   echo -n "${blue}Waiting for instack's dhcp address${reset}"
499   while ! grep instack /var/lib/libvirt/dnsmasq/default.leases > /dev/null && [ $CNT -gt 0 ]; do
500       echo -n "."
501       sleep 3
502       CNT=CNT-1
503   done
504
505   # get the instack VM IP
506   UNDERCLOUD=$(grep instack /var/lib/libvirt/dnsmasq/default.leases | awk '{print $3}' | head -n 1)
507   if [ -z "$UNDERCLOUD" ]; then
508     #if not found then dnsmasq may be using leasefile-ro
509     instack_mac=$(virsh domiflist instack | grep default | \
510                   grep -Eo "[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+:[0-9a-f\]+")
511     UNDERCLOUD=$(/usr/sbin/arp -e | grep ${instack_mac} | awk {'print $1'})
512
513     if [ -z "$UNDERCLOUD" ]; then
514       echo "\n\nNever got IP for Instack. Can Not Continue."
515       exit 1
516     else
517       echo -e "${blue}\rInstack VM has IP $UNDERCLOUD${reset}"
518     fi
519   else
520      echo -e "${blue}\rInstack VM has IP $UNDERCLOUD${reset}"
521   fi
522
523   CNT=10
524   echo -en "${blue}\rValidating instack VM connectivity${reset}"
525   while ! ping -c 1 $UNDERCLOUD > /dev/null && [ $CNT -gt 0 ]; do
526       echo -n "."
527       sleep 3
528       CNT=$CNT-1
529   done
530   if [ "$CNT" -eq 0 ]; then
531       echo "Failed to contact Instack. Can Not Continue"
532       exit 1
533   fi
534   CNT=10
535   while ! ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "echo ''" 2>&1> /dev/null && [ $CNT -gt 0 ]; do
536       echo -n "."
537       sleep 3
538       CNT=$CNT-1
539   done
540   if [ "$CNT" -eq 0 ]; then
541       echo "Failed to connect to Instack. Can Not Continue"
542       exit 1
543   fi
544
545   # extra space to overwrite the previous connectivity output
546   echo -e "${blue}\r                                                                 ${reset}"
547
548   #add the instack public interface if net isolation is enabled (more than just admin network)
549   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
550     virsh attach-interface --domain instack --type network --source ${NET_MAP['public_network']} --model rtl8139 --config --live
551     sleep 1
552     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"
553   fi
554   # ssh key fix for stack user
555   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "restorecon -r /home/stack"
556 }
557
558 ##Create virtual nodes in virsh
559 ##params: none
560 function setup_virtual_baremetal {
561   for i in $(seq 0 $vm_index); do
562     if ! virsh list --all | grep baremetalbrbm_brbm1_brbm2_brbm3_${i} > /dev/null; then
563       if [ ! -e $CONFIG/baremetalbrbm_brbm1_brbm2_brbm3_${i}.xml ]; then
564         define_virtual_node baremetalbrbm_brbm1_brbm2_brbm3_${i}
565       fi
566       # Fix for ramdisk using wrong pxeboot interface
567       # TODO: revisit this and see if there's a more proper fix
568       sed -i "/^\s*<source network='brbm2'\/>/{
569         N
570         s/^\(.*\)virtio\(.*\)$/\1rtl8139\2/
571         }" $CONFIG/baremetalbrbm_brbm1_brbm2_brbm3_${i}.xml
572       virsh define $CONFIG/baremetalbrbm_brbm1_brbm2_brbm3_${i}.xml
573     else
574       echo "Found Baremetal ${i} VM, using existing VM"
575     fi
576     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
577   done
578
579 }
580
581 ##Set network-environment settings
582 ##params: network-environment file to edit
583 function configure_network_environment {
584   sed -i '/ControlPlaneSubnetCidr/c\\  ControlPlaneSubnetCidr: "'${admin_network_cidr##*/}'"' $1
585   sed -i '/ControlPlaneDefaultRoute/c\\  ControlPlaneDefaultRoute: '${admin_network_provisioner_ip}'' $1
586   sed -i '/ExternalNetCidr/c\\  ExternalNetCidr: '${public_network_cidr}'' $1
587   sed -i "/ExternalAllocationPools/c\\  ExternalAllocationPools: [{'start': '${public_network_usable_ip_range%%,*}', 'end': '${public_network_usable_ip_range##*,}'}]" $1
588   sed -i '/ExternalInterfaceDefaultRoute/c\\  ExternalInterfaceDefaultRoute: '${public_network_gateway}'' $1
589 }
590 ##Copy over the glance images and instack json file
591 ##params: none
592 function configure_undercloud {
593
594   echo
595   echo "Copying configuration file and disk images to instack"
596   scp ${SSH_OPTIONS[@]} $RESOURCES/overcloud-full.qcow2 "stack@$UNDERCLOUD":
597   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
598     configure_network_environment $CONFIG/network-environment.yaml
599     echo -e "${blue}Network Environment set for Deployment: ${reset}"
600     cat $CONFIG/network-environment.yaml
601     scp ${SSH_OPTIONS[@]} $CONFIG/network-environment.yaml "stack@$UNDERCLOUD":
602   fi
603   scp ${SSH_OPTIONS[@]} -r $CONFIG/nics/ "stack@$UNDERCLOUD":
604
605   # ensure stack user on instack machine has an ssh key
606   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "if [ ! -e ~/.ssh/id_rsa.pub ]; then ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa; fi"
607
608   if [ "$virtual" == "TRUE" ]; then
609
610       # copy the instack vm's stack user's pub key to
611       # root's auth keys so that instack can control
612       # vm power on the hypervisor
613       ssh ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "cat /home/stack/.ssh/id_rsa.pub" >> /root/.ssh/authorized_keys
614
615       # fix MACs to match new setup
616       for i in $(seq 0 $vm_index); do
617         pyscript="import json
618 data = json.load(open('$CONFIG/instackenv-virt.json'))
619 print data['nodes'][$i]['mac'][0]"
620
621         old_mac=$(python -c "$pyscript")
622         new_mac=$(virsh dumpxml baremetalbrbm_brbm1_brbm2_brbm3_$i | grep "mac address" | cut -d = -f2 | grep -Eo "[0-9a-f:]+")
623         # this doesn't work with multiple vnics on the vms
624         #if [ "$old_mac" != "$new_mac" ]; then
625         #  echo "${blue}Modifying MAC for node from $old_mac to ${new_mac}${reset}"
626         #  sed -i 's/'"$old_mac"'/'"$new_mac"'/' $CONFIG/instackenv-virt.json
627         #fi
628       done
629
630       DEPLOY_OPTIONS+=" --libvirt-type qemu"
631       INSTACKENV=$CONFIG/instackenv-virt.json
632
633       # upload instackenv file to Instack for virtual deployment
634       scp ${SSH_OPTIONS[@]} $INSTACKENV "stack@$UNDERCLOUD":instackenv.json
635   fi
636
637   # allow stack to control power management on the hypervisor via sshkey
638   # only if this is a virtual deployment
639   if [ "$virtual" == "TRUE" ]; then
640       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
641 while read -r line; do
642   stack_key=\${stack_key}\\\\\\\\n\${line}
643 done < <(cat ~/.ssh/id_rsa)
644 stack_key=\$(echo \$stack_key | sed 's/\\\\\\\\n//')
645 sed -i 's~INSERT_STACK_USER_PRIV_KEY~'"\$stack_key"'~' instackenv.json
646 EOI
647   fi
648
649   # copy stack's ssh key to this users authorized keys
650   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "cat /home/stack/.ssh/id_rsa.pub" >> ~/.ssh/authorized_keys
651
652   # disable requiretty for sudo
653   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "sed -i 's/Defaults\s*requiretty//'" /etc/sudoers
654
655   # configure undercloud on Undercloud VM
656   echo "Running undercloud configuration."
657   echo "Logging undercloud configuration to instack:/home/stack/apex-undercloud-install.log"
658   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI
659 if [[ "$net_isolation_enabled" == "TRUE" ]]; then
660   sed -i 's/#local_ip/local_ip/' undercloud.conf
661   sed -i 's/#network_gateway/network_gateway/' undercloud.conf
662   sed -i 's/#network_cidr/network_cidr/' undercloud.conf
663   sed -i 's/#dhcp_start/dhcp_start/' undercloud.conf
664   sed -i 's/#dhcp_end/dhcp_end/' undercloud.conf
665   sed -i 's/#inspection_iprange/inspection_iprange/' undercloud.conf
666   sed -i 's/#undercloud_debug/undercloud_debug/' undercloud.conf
667
668   openstack-config --set undercloud.conf DEFAULT local_ip ${admin_network_provisioner_ip}/${admin_network_cidr##*/}
669   openstack-config --set undercloud.conf DEFAULT network_gateway ${admin_network_provisioner_ip}
670   openstack-config --set undercloud.conf DEFAULT network_cidr ${admin_network_cidr}
671   openstack-config --set undercloud.conf DEFAULT dhcp_start ${admin_network_dhcp_range%%,*}
672   openstack-config --set undercloud.conf DEFAULT dhcp_end ${admin_network_dhcp_range##*,}
673   openstack-config --set undercloud.conf DEFAULT inspection_iprange ${admin_network_introspection_range}
674   openstack-config --set undercloud.conf DEFAULT undercloud_debug false
675 fi
676
677 openstack undercloud install &> apex-undercloud-install.log
678 sleep 30
679 sudo systemctl restart openstack-glance-api
680 sudo systemctl restart openstack-nova-conductor
681 sudo systemctl restart openstack-nova-compute
682 EOI
683 # WORKAROUND: must restart the above services to fix sync problem with nova compute manager
684 # TODO: revisit and file a bug if necessary. This should eventually be removed
685 # as well as glance api problem
686 echo -e "${blue}INFO: Sleeping 15 seconds while services come back from restart${reset}"
687 sleep 15
688 #TODO Fill in the rest of the network-environment values for other networks
689
690 }
691
692 ##preping it for deployment and launch the deploy
693 ##params: none
694 function undercloud_prep_overcloud_deploy {
695
696   if [[ ${#deploy_options_array[@]} -eq 0 || ${deploy_options_array['sdn_controller']} == 'opendaylight' ]]; then
697     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight.yaml"
698   elif [ ${deploy_options_array['sdn_controller']} == 'opendaylight-external' ]; then
699     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight-external.yaml"
700   elif [ ${deploy_options_array['sdn_controller']} == 'onos' ]; then
701     echo -e "${red}ERROR: ONOS is currently unsupported...exiting${reset}"
702     exit 1
703   elif [ ${deploy_options_array['sdn_controller']} == 'opencontrail' ]; then
704     echo -e "${red}ERROR: OpenContrail is currently unsupported...exiting${reset}"
705     exit 1
706   fi
707
708   # check if HA is enabled
709   if [[ "$ha_enabled" == "TRUE" ]]; then
710      DEPLOY_OPTIONS+=" --control-scale 3 --compute-scale 2"
711      DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/puppet-pacemaker.yaml"
712   fi
713
714   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
715      #DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/network-isolation.yaml"
716      DEPLOY_OPTIONS+=" -e network-environment.yaml"
717   fi
718
719   if [[ "$ha_enabled" == "TRUE" ]] || [[ "$net_isolation_enabled" == "TRUE" ]]; then
720      DEPLOY_OPTIONS+=" --ntp-server $ntp_server"
721   fi
722
723   if [[ ! "$virtual" == "TRUE" ]]; then
724      DEPLOY_OPTIONS+=" --control-flavor control --compute-flavor compute"
725   fi
726
727   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
728 source stackrc
729 set -o errexit
730 echo "Uploading overcloud glance images"
731 openstack overcloud image upload
732 echo "Configuring undercloud and discovering nodes"
733 openstack baremetal import --json instackenv.json
734 openstack baremetal configure boot
735 openstack baremetal introspection bulk start
736 echo "Configuring flavors"
737 for flavor in baremetal control compute; do
738   echo -e "${blue}INFO: Updating flavor: \${flavor}${reset}"
739   if openstack flavor list | grep \${flavor}; then
740     openstack flavor delete \${flavor}
741   fi
742   openstack flavor create --id auto --ram 4096 --disk 39 --vcpus 1 \${flavor}
743   if ! openstack flavor list | grep \${flavor}; then
744     echo -e "${red}ERROR: Unable to create flavor \${flavor}${reset}"
745   fi
746 done
747 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" baremetal
748 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="control" control
749 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="compute" compute
750 echo "Configuring nameserver on ctlplane network"
751 neutron subnet-update \$(neutron subnet-list | grep -v id | grep -v \\\\-\\\\- | awk {'print \$2'}) --dns-nameserver 8.8.8.8
752 echo "Executing overcloud deployment, this should run for an extended period without output."
753 sleep 60 #wait for Hypervisor stats to check-in to nova
754 openstack overcloud deploy --templates $DEPLOY_OPTIONS
755 EOI
756
757 }
758
759 display_usage() {
760   echo -e "Usage:\n$0 [arguments] \n"
761   echo -e "   -c|--config : Directory to configuration files. Optional.  Defaults to /var/opt/opnfv/ \n"
762   echo -e "   -d|--deploy-settings : Full path to deploy settings yaml file. Optional.  Defaults to null \n"
763   echo -e "   -i|--inventory : Full path to inventory yaml file. Required only for baremetal \n"
764   echo -e "   -n|--net-settings : Full path to network settings file. Optional. \n"
765   echo -e "   -p|--ping-site : site to use to verify IP connectivity. Optional. Defaults to 8.8.8.8 \n"
766   echo -e "   -r|--resources : Directory to deployment resources. Optional.  Defaults to /var/opt/opnfv/stack \n"
767   echo -e "   -v|--virtual : Virtualize overcloud nodes instead of using baremetal. \n"
768   echo -e "   --no-ha : disable High Availability deployment scheme, this assumes a single controller and single compute node \n"
769   echo -e "   --flat : disable Network Isolation and use a single flat network for the underlay network."
770 }
771
772 ##translates the command line parameters into variables
773 ##params: $@ the entire command line is passed
774 ##usage: parse_cmd_line() "$@"
775 parse_cmdline() {
776   echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n"
777   echo "Use -h to display help"
778   sleep 2
779
780   while [ "${1:0:1}" = "-" ]
781   do
782     case "$1" in
783         -h|--help)
784                 display_usage
785                 exit 0
786             ;;
787         -c|--config)
788                 CONFIG=$2
789                 echo "Deployment Configuration Directory Overridden to: $2"
790                 shift 2
791             ;;
792         -d|--deploy-settings)
793                 DEPLOY_SETTINGS_FILE=$2
794                 echo "Deployment Configuration file: $2"
795                 shift 2
796             ;;
797         -i|--inventory)
798                 INVENTORY_FILE=$2
799                 shift 2
800             ;;
801         -n|--net-settings)
802                 NETSETS=$2
803                 echo "Network Settings Configuration file: $2"
804                 shift 2
805             ;;
806         -p|--ping-site)
807                 ping_site=$2
808                 echo "Using $2 as the ping site"
809                 shift 2
810             ;;
811         -r|--resources)
812                 RESOURCES=$2
813                 echo "Deployment Resources Directory Overridden to: $2"
814                 shift 2
815             ;;
816         -v|--virtual)
817                 virtual="TRUE"
818                 echo "Executing a Virtual Deployment"
819                 shift 1
820             ;;
821         --no-ha )
822                 ha_enabled="FALSE"
823                 echo "HA Deployment Disabled"
824                 shift 1
825             ;;
826         --flat )
827                 net_isolation_enabled="FALSE"
828                 echo "Underlay Network Isolation Disabled: using flat configuration"
829                 shift 1
830             ;;
831         *)
832                 display_usage
833                 exit 1
834             ;;
835     esac
836   done
837
838   if [[ ! -z "$NETSETS" && "$net_isolation_enabled" == "FALSE" ]]; then
839     echo -e "${red}INFO: Single flat network requested. Ignoring any network settings!${reset}"
840   elif [[ -z "$NETSETS" && "$net_isolation_enabled" == "TRUE" ]]; then
841     echo -e "${red}ERROR: You must provide a network_settings file with -n or use --flat to force a single flat network{reset}"
842   fi
843
844   if [[ -n "$virtual" && -n "$INVENTORY_FILE" ]]; then
845     echo -e "${red}ERROR: You should not specify an inventory with virtual deployments${reset}"
846     exit 1
847   fi
848
849   if [[ ! -z "$DEPLOY_SETTINGS_FILE" && ! -f "$DEPLOY_SETTINGS_FILE" ]]; then
850     echo -e "${red}ERROR: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
851     exit 1
852   fi
853
854   if [[ ! -z "$NETSETS" && ! -f "$NETSETS" ]]; then
855     echo -e "${red}ERROR: ${NETSETS} does not exist! Exiting...${reset}"
856     exit 1
857   fi
858
859   if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
860     echo -e "{$red}ERROR: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
861     exit 1
862   fi
863
864   if [[ -z "$virtual" && -z "$INVENTORY_FILE" ]]; then
865     echo -e "${red}ERROR: You must specify an inventory file for baremetal deployments! Exiting...${reset}"
866     exit 1
867   fi
868 }
869
870 ##END FUNCTIONS
871
872 main() {
873   parse_cmdline "$@"
874   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
875     echo -e "${blue}INFO: Parsing network settings file...${reset}"
876     parse_network_settings
877   fi
878   if ! configure_deps; then
879     echo -e "${red}Dependency Validation Failed, Exiting.${reset}"
880     exit 1
881   fi
882   if [ -n "$DEPLOY_SETTINGS_FILE" ]; then
883     parse_deploy_settings
884   fi
885   setup_instack_vm
886   if [ "$virtual" == "TRUE" ]; then
887     setup_virtual_baremetal
888   elif [ -n "$INVENTORY_FILE" ]; then
889     parse_inventory_file
890   fi
891   configure_undercloud
892   undercloud_prep_overcloud_deploy
893 }
894
895 main "$@"