Adds onos support
[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     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/onos.yaml"
702   elif [ ${deploy_options_array['sdn_controller']} == 'opencontrail' ]; then
703     echo -e "${red}ERROR: OpenContrail is currently unsupported...exiting${reset}"
704     exit 1
705   fi
706
707   # check if HA is enabled
708   if [[ "$ha_enabled" == "TRUE" ]]; then
709      DEPLOY_OPTIONS+=" --control-scale 3 --compute-scale 2"
710      DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/puppet-pacemaker.yaml"
711   fi
712
713   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
714      #DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/network-isolation.yaml"
715      DEPLOY_OPTIONS+=" -e network-environment.yaml"
716   fi
717
718   if [[ "$ha_enabled" == "TRUE" ]] || [[ "$net_isolation_enabled" == "TRUE" ]]; then
719      DEPLOY_OPTIONS+=" --ntp-server $ntp_server"
720   fi
721
722   if [[ ! "$virtual" == "TRUE" ]]; then
723      DEPLOY_OPTIONS+=" --control-flavor control --compute-flavor compute"
724   fi
725
726   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
727 source stackrc
728 set -o errexit
729 echo "Uploading overcloud glance images"
730 openstack overcloud image upload
731 echo "Configuring undercloud and discovering nodes"
732 openstack baremetal import --json instackenv.json
733 openstack baremetal configure boot
734 openstack baremetal introspection bulk start
735 echo "Configuring flavors"
736 for flavor in baremetal control compute; do
737   echo -e "${blue}INFO: Updating flavor: \${flavor}${reset}"
738   if openstack flavor list | grep \${flavor}; then
739     openstack flavor delete \${flavor}
740   fi
741   openstack flavor create --id auto --ram 4096 --disk 39 --vcpus 1 \${flavor}
742   if ! openstack flavor list | grep \${flavor}; then
743     echo -e "${red}ERROR: Unable to create flavor \${flavor}${reset}"
744   fi
745 done
746 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" baremetal
747 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="control" control
748 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="compute" compute
749 echo "Configuring nameserver on ctlplane network"
750 neutron subnet-update \$(neutron subnet-list | grep -v id | grep -v \\\\-\\\\- | awk {'print \$2'}) --dns-nameserver 8.8.8.8
751 echo "Executing overcloud deployment, this should run for an extended period without output."
752 sleep 60 #wait for Hypervisor stats to check-in to nova
753 openstack overcloud deploy --templates $DEPLOY_OPTIONS
754 EOI
755
756 }
757
758 display_usage() {
759   echo -e "Usage:\n$0 [arguments] \n"
760   echo -e "   -c|--config : Directory to configuration files. Optional.  Defaults to /var/opt/opnfv/ \n"
761   echo -e "   -d|--deploy-settings : Full path to deploy settings yaml file. Optional.  Defaults to null \n"
762   echo -e "   -i|--inventory : Full path to inventory yaml file. Required only for baremetal \n"
763   echo -e "   -n|--net-settings : Full path to network settings file. Optional. \n"
764   echo -e "   -p|--ping-site : site to use to verify IP connectivity. Optional. Defaults to 8.8.8.8 \n"
765   echo -e "   -r|--resources : Directory to deployment resources. Optional.  Defaults to /var/opt/opnfv/stack \n"
766   echo -e "   -v|--virtual : Virtualize overcloud nodes instead of using baremetal. \n"
767   echo -e "   --no-ha : disable High Availability deployment scheme, this assumes a single controller and single compute node \n"
768   echo -e "   --flat : disable Network Isolation and use a single flat network for the underlay network."
769 }
770
771 ##translates the command line parameters into variables
772 ##params: $@ the entire command line is passed
773 ##usage: parse_cmd_line() "$@"
774 parse_cmdline() {
775   echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n"
776   echo "Use -h to display help"
777   sleep 2
778
779   while [ "${1:0:1}" = "-" ]
780   do
781     case "$1" in
782         -h|--help)
783                 display_usage
784                 exit 0
785             ;;
786         -c|--config)
787                 CONFIG=$2
788                 echo "Deployment Configuration Directory Overridden to: $2"
789                 shift 2
790             ;;
791         -d|--deploy-settings)
792                 DEPLOY_SETTINGS_FILE=$2
793                 echo "Deployment Configuration file: $2"
794                 shift 2
795             ;;
796         -i|--inventory)
797                 INVENTORY_FILE=$2
798                 shift 2
799             ;;
800         -n|--net-settings)
801                 NETSETS=$2
802                 echo "Network Settings Configuration file: $2"
803                 shift 2
804             ;;
805         -p|--ping-site)
806                 ping_site=$2
807                 echo "Using $2 as the ping site"
808                 shift 2
809             ;;
810         -r|--resources)
811                 RESOURCES=$2
812                 echo "Deployment Resources Directory Overridden to: $2"
813                 shift 2
814             ;;
815         -v|--virtual)
816                 virtual="TRUE"
817                 echo "Executing a Virtual Deployment"
818                 shift 1
819             ;;
820         --no-ha )
821                 ha_enabled="FALSE"
822                 echo "HA Deployment Disabled"
823                 shift 1
824             ;;
825         --flat )
826                 net_isolation_enabled="FALSE"
827                 echo "Underlay Network Isolation Disabled: using flat configuration"
828                 shift 1
829             ;;
830         *)
831                 display_usage
832                 exit 1
833             ;;
834     esac
835   done
836
837   if [[ ! -z "$NETSETS" && "$net_isolation_enabled" == "FALSE" ]]; then
838     echo -e "${red}INFO: Single flat network requested. Ignoring any network settings!${reset}"
839   elif [[ -z "$NETSETS" && "$net_isolation_enabled" == "TRUE" ]]; then
840     echo -e "${red}ERROR: You must provide a network_settings file with -n or use --flat to force a single flat network{reset}"
841   fi
842
843   if [[ -n "$virtual" && -n "$INVENTORY_FILE" ]]; then
844     echo -e "${red}ERROR: You should not specify an inventory with virtual deployments${reset}"
845     exit 1
846   fi
847
848   if [[ ! -z "$DEPLOY_SETTINGS_FILE" && ! -f "$DEPLOY_SETTINGS_FILE" ]]; then
849     echo -e "${red}ERROR: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
850     exit 1
851   fi
852
853   if [[ ! -z "$NETSETS" && ! -f "$NETSETS" ]]; then
854     echo -e "${red}ERROR: ${NETSETS} does not exist! Exiting...${reset}"
855     exit 1
856   fi
857
858   if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
859     echo -e "{$red}ERROR: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
860     exit 1
861   fi
862
863   if [[ -z "$virtual" && -z "$INVENTORY_FILE" ]]; then
864     echo -e "${red}ERROR: You must specify an inventory file for baremetal deployments! Exiting...${reset}"
865     exit 1
866   fi
867 }
868
869 ##END FUNCTIONS
870
871 main() {
872   parse_cmdline "$@"
873   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
874     echo -e "${blue}INFO: Parsing network settings file...${reset}"
875     parse_network_settings
876   fi
877   if ! configure_deps; then
878     echo -e "${red}Dependency Validation Failed, Exiting.${reset}"
879     exit 1
880   fi
881   if [ -n "$DEPLOY_SETTINGS_FILE" ]; then
882     parse_deploy_settings
883   fi
884   setup_instack_vm
885   if [ "$virtual" == "TRUE" ]; then
886     setup_virtual_baremetal
887   elif [ -n "$INVENTORY_FILE" ]; then
888     parse_inventory_file
889   fi
890   configure_undercloud
891   undercloud_prep_overcloud_deploy
892 }
893
894 main "$@"