Migrating to Mitaka
[apex.git] / ci / deploy.sh
1 #!/bin/bash
2 ##############################################################################
3 # Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
10
11 # Deploy script to install provisioning server for OPNFV Apex
12 # author: Dan Radez (dradez@redhat.com)
13 # author: Tim Rozet (trozet@redhat.com)
14 #
15 # Based on RDO Manager http://www.rdoproject.org
16
17 set -e
18
19 ##VARIABLES
20 reset=$(tput sgr0 || echo "")
21 blue=$(tput setaf 4 || echo "")
22 red=$(tput setaf 1 || echo "")
23 green=$(tput setaf 2 || echo "")
24
25 vm_index=4
26 interactive="FALSE"
27 ping_site="8.8.8.8"
28 ntp_server="pool.ntp.org"
29 net_isolation_enabled="TRUE"
30 post_config="TRUE"
31 debug="FALSE"
32 nics_cfg=''
33
34 declare -i CNT
35 declare UNDERCLOUD
36 declare -A deploy_options_array
37 declare -A NET_MAP
38
39 SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error)
40 DEPLOY_OPTIONS=""
41 RESOURCES=/var/opt/opnfv/images
42 CONFIG=/var/opt/opnfv
43 OPNFV_NETWORK_TYPES="admin_network private_network public_network storage_network"
44 # Netmap used to map networks to OVS bridge names
45 NET_MAP['admin_network']="br-admin"
46 NET_MAP['private_network']="br-private"
47 NET_MAP['public_network']="br-public"
48 NET_MAP['storage_network']="br-storage"
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' && "$net_isolation_enabled" == "TRUE" ]]; then
118       echo -e "${red}ERROR: You must enable public_network and configure it explicitly or use auto-detection${reset}"
119       exit 1
120     else
121       echo -e "${blue}INFO: Network: ${network} is disabled, will collapse into admin_network"
122     fi
123   done
124
125   # check for enabled network values
126   for enabled_network in ${enabled_network_list}; do
127     # detect required settings first to continue
128     echo -e "${blue}INFO: Detecting Required settings for: ${enabled_network}${reset}"
129     for setting in ${required_network_settings}; do
130       eval "setting_value=\${${enabled_network}_${setting}}"
131       if [ -z "${setting_value}" ]; then
132         # if setting is missing we try to autodetect
133         eval "nic_value=\${${enabled_network}_bridged_interface}"
134         if [ -n "$nic_value" ]; then
135           setting_value=$(eval find_${setting} ${nic_value})
136           if [ -n "$setting_value" ]; then
137             eval "${enabled_network}_${setting}=${setting_value}"
138             echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
139           else
140             echo -e "${red}ERROR: Auto-detection failed: ${setting} not found using interface: ${nic_value}${reset}"
141             exit 1
142           fi
143         else
144           echo -e "${red}ERROR: Required setting: ${setting} not found, and bridge interface not provided\
145 for Auto-detection${reset}"
146           exit 1
147         fi
148       else
149         echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
150       fi
151     done
152     echo -e "${blue}INFO: Detecting Common settings for: ${enabled_network}${reset}"
153     # detect optional common settings
154     # these settings can be auto-generated if missing
155     for setting in ${common_optional_network_settings}; do
156       eval "setting_value=\${${enabled_network}_${setting}}"
157       if [ -z "${setting_value}" ]; then
158         if [ -n "$nic_value" ]; then
159           setting_value=$(eval find_${setting} ${nic_value})
160         else
161           setting_value=''
162           echo -e "${blue}INFO: Skipping Auto-detection, NIC not specified for ${enabled_network}.  Attempting Auto-generation...${reset}"
163         fi
164         if [ -n "$setting_value" ]; then
165           eval "${enabled_network}_${setting}=${setting_value}"
166           echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
167         else
168           # if Auto-detection fails we can auto-generate with CIDR
169           eval "cidr=\${${enabled_network}_cidr}"
170           if [ -n "$cidr" ]; then
171             echo -e "${blue}INFO: Auto-generating: ${setting}${reset}"
172             setting_value=$(eval generate_${setting} ${cidr})
173           else
174             setting_value=''
175             echo -e "${red}ERROR: Auto-generation failed: required parameter CIDR missing for network ${enabled_network}${reset}"
176           fi
177           if [ -n "$setting_value" ]; then
178             eval "${enabled_network}_${setting}=${setting_value}"
179             echo -e "${blue}INFO: Auto-generated: ${enabled_network}_${setting}: ${setting_value}${reset}"
180           else
181             echo -e "${red}ERROR: Auto-generation failed: ${setting} not found${reset}"
182             exit 1
183           fi
184         fi
185       else
186         echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
187       fi
188     done
189     echo -e "${blue}INFO: Detecting Network Specific settings for: ${enabled_network}${reset}"
190     # detect network specific settings
191     for setting in $(eval echo \${${enabled_network}_optional_settings}); do
192       eval "setting_value=\${${enabled_network}_${setting}}"
193       if [ -z "${setting_value}" ]; then
194         if [ -n "$nic_value" ]; then
195           setting_value=$(eval find_${setting} ${nic_value})
196         else
197           setting_value=''
198           echo -e "${blue}INFO: Skipping Auto-detection, NIC not specified for ${enabled_network}.  Attempting Auto-generation...${reset}"
199         fi
200         if [ -n "$setting_value" ]; then
201           eval "${enabled_network}_${setting}=${setting_value}"
202           echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
203         else
204           eval "cidr=\${${enabled_network}_cidr}"
205           if [ -n "$cidr" ]; then
206             setting_value=$(eval generate_${setting} ${cidr})
207           else
208             setting_value=''
209             echo -e "${red}ERROR: Auto-generation failed: required parameter CIDR missing for network ${enabled_network}${reset}"
210           fi
211           if [ -n "$setting_value" ]; then
212             eval "${enabled_network}_${setting}=${setting_value}"
213             echo -e "${blue}INFO: Auto-generated: ${enabled_network}_${setting}: ${setting_value}${reset}"
214           else
215             echo -e "${red}ERROR: Auto-generation failed: ${setting} not found${reset}"
216             exit 1
217           fi
218         fi
219       else
220         echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
221       fi
222     done
223   done
224 }
225 ##parses deploy settings yaml into globals and options array
226 ##params: none
227 ##usage:  parse_deploy_settings
228 parse_deploy_settings() {
229   local global_prefix="deploy_global_params_"
230   local options_prefix="deploy_deploy_options_"
231   local myvar myvalue
232   local settings=$(parse_yaml $DEPLOY_SETTINGS_FILE "deploy_")
233
234   for this_setting in $settings; do
235     if contains_prefix $this_setting $global_prefix; then
236       myvar=$(parse_setting_var $this_setting $global_prefix)
237       if [ -z "$myvar" ]; then
238         echo -e "${red}ERROR: while parsing ${DEPLOY_SETTINGS_FILE} for setting: ${this_setting}${reset}"
239       fi
240       myvalue=$(parse_setting_value $this_setting)
241       # Do not override variables set by cmdline
242       if [ -z "$(eval echo \$$myvar)" ]; then
243         eval "$myvar=\$myvalue"
244         echo -e "${blue}Global parameter set: ${myvar}:${myvalue}${reset}"
245       else
246         echo -e "${blue}Global parameter already set: ${myvar}${reset}"
247       fi
248     elif contains_prefix $this_setting $options_prefix; then
249       myvar=$(parse_setting_var $this_setting $options_prefix)
250       if [ -z "$myvar" ]; then
251         echo -e "${red}ERROR: while parsing ${DEPLOY_SETTINGS_FILE} for setting: ${this_setting}${reset}"
252       fi
253       myvalue=$(parse_setting_value $this_setting)
254       deploy_options_array[$myvar]=$myvalue
255       echo -e "${blue}Deploy option set: ${myvar}:${myvalue}${reset}"
256     fi
257   done
258 }
259 ##parses baremetal yaml settings into compatible json
260 ##writes the json to $CONFIG/instackenv_tmp.json
261 ##params: none
262 ##usage: parse_inventory_file
263 parse_inventory_file() {
264   local inventory=$(parse_yaml $INVENTORY_FILE)
265   local node_list
266   local node_prefix="node"
267   local node_count=0
268   local node_total
269   local inventory_list
270
271   # detect number of nodes
272   for entry in $inventory; do
273     if echo $entry | grep -Eo "^nodes_node[0-9]+_" > /dev/null; then
274       this_node=$(echo $entry | grep -Eo "^nodes_node[0-9]+_")
275       if [[ "$inventory_list" != *"$this_node"* ]]; then
276         inventory_list+="$this_node "
277       fi
278     fi
279   done
280
281   inventory_list=$(echo $inventory_list | sed 's/ $//')
282
283   for node in $inventory_list; do
284     ((node_count+=1))
285   done
286
287   node_total=$node_count
288
289   if [[ "$node_total" -lt 5 && ( ha_enabled == "TRUE" || "$ha_enabled" == "true" ) ]]; then
290     echo -e "${red}ERROR: You must provide at least 5 nodes for HA baremetal deployment${reset}"
291     exit 1
292   elif [[ "$node_total" -lt 2 ]]; then
293     echo -e "${red}ERROR: You must provide at least 2 nodes for non-HA baremetal deployment${reset}"
294     exit 1
295   fi
296
297   eval $(parse_yaml $INVENTORY_FILE) || {
298     echo "${red}Failed to parse inventory.yaml. Aborting.${reset}"
299     exit 1
300   }
301
302   instackenv_output="
303 {
304  \"nodes\" : [
305
306 "
307   node_count=0
308   for node in $inventory_list; do
309     ((node_count+=1))
310     node_output="
311         {
312           \"pm_password\": \"$(eval echo \${${node}ipmi_pass})\",
313           \"pm_type\": \"$(eval echo \${${node}pm_type})\",
314           \"mac\": [
315             \"$(eval echo \${${node}mac_address})\"
316           ],
317           \"cpu\": \"$(eval echo \${${node}cpus})\",
318           \"memory\": \"$(eval echo \${${node}memory})\",
319           \"disk\": \"$(eval echo \${${node}disk})\",
320           \"arch\": \"$(eval echo \${${node}arch})\",
321           \"pm_user\": \"$(eval echo \${${node}ipmi_user})\",
322           \"pm_addr\": \"$(eval echo \${${node}ipmi_ip})\",
323           \"capabilities\": \"$(eval echo \${${node}capabilities})\"
324 "
325     instackenv_output+=${node_output}
326     if [ $node_count -lt $node_total ]; then
327       instackenv_output+="        },"
328     else
329       instackenv_output+="        }"
330     fi
331   done
332
333   instackenv_output+='
334   ]
335 }
336 '
337   #Copy instackenv.json to undercloud for baremetal
338   echo -e "{blue}Parsed instackenv JSON:\n${instackenv_output}${reset}"
339   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
340 cat > instackenv.json << EOF
341 $instackenv_output
342 EOF
343 EOI
344
345 }
346 ##verify internet connectivity
347 #params: none
348 function verify_internet {
349   if ping -c 2 $ping_site > /dev/null; then
350     if ping -c 2 www.google.com > /dev/null; then
351       echo "${blue}Internet connectivity detected${reset}"
352       return 0
353     else
354       echo "${red}Internet connectivity detected, but DNS lookup failed${reset}"
355       return 1
356     fi
357   else
358     echo "${red}No internet connectivity detected${reset}"
359     return 1
360   fi
361 }
362
363 ##download dependencies if missing and configure host
364 #params: none
365 function configure_deps {
366   if ! verify_internet; then
367     echo "${red}Will not download dependencies${reset}"
368     internet=false
369   fi
370
371   # verify ip forwarding
372   if sysctl net.ipv4.ip_forward | grep 0; then
373     sudo sysctl -w net.ipv4.ip_forward=1
374     sudo sh -c "echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf"
375   fi
376
377   # ensure no dhcp server is running on jumphost
378   if ! sudo systemctl status dhcpd | grep dead; then
379     echo "${red}WARN: DHCP Server detected on jumphost, disabling...${reset}"
380     sudo systemctl stop dhcpd
381     sudo systemctl disable dhcpd
382   fi
383
384   # ensure networks are configured
385   systemctl status libvirtd || systemctl start libvirtd
386   systemctl status openvswitch || systemctl start openvswitch
387
388   # If flat we only use admin network
389   if [[ "$net_isolation_enabled" == "FALSE" ]]; then
390     virsh_enabled_networks="admin_network"
391     enabled_network_list="admin_network"
392   # For baremetal we only need to create/attach Undercloud to admin and public
393   elif [ "$virtual" == "FALSE" ]; then
394     virsh_enabled_networks="admin_network public_network"
395   else
396     virsh_enabled_networks=$enabled_network_list
397   fi
398
399   # ensure default network is configured correctly
400   libvirt_dir="/usr/share/libvirt/networks"
401   virsh net-list --all | grep default || virsh net-define ${libvirt_dir}/default.xml
402   virsh net-list --all | grep -E "default\s+active" > /dev/null || virsh net-start default
403   virsh net-list --all | grep -E "default\s+active\s+yes" > /dev/null || virsh net-autostart --network default
404
405   for network in ${OPNFV_NETWORK_TYPES}; do
406     echo "${blue}INFO: Creating Virsh Network: $network & OVS Bridge: ${NET_MAP[$network]}${reset}"
407     ovs-vsctl list-br | grep ${NET_MAP[$network]} > /dev/null || ovs-vsctl add-br ${NET_MAP[$network]}
408     virsh net-list --all | grep $network > /dev/null || (cat > ${libvirt_dir}/apex-virsh-net.xml && virsh net-define ${libvirt_dir}/apex-virsh-net.xml) << EOF
409 <network>
410   <name>$network</name>
411   <forward mode='bridge'/>
412   <bridge name='${NET_MAP[$network]}'/>
413   <virtualport type='openvswitch'/>
414 </network>
415 EOF
416     if ! (virsh net-list --all | grep $network > /dev/null); then
417         echo "${red}ERROR: unable to create network: ${network}${reset}"
418         exit 1;
419     fi
420     rm -f ${libvirt_dir}/apex-virsh-net.xml &> /dev/null;
421     virsh net-list | grep -E "$network\s+active" > /dev/null || virsh net-start $network
422     virsh net-list | grep -E "$network\s+active\s+yes" > /dev/null || virsh net-autostart --network $network
423   done
424
425   echo -e "${blue}INFO: Bridges set: ${reset}"
426   ovs-vsctl list-br
427   echo -e "${blue}INFO: virsh networks set: ${reset}"
428   virsh net-list
429
430   if [[ -z "$virtual" || "$virtual" == "FALSE" ]]; then
431     # bridge interfaces to correct OVS instances for baremetal deployment
432     for network in ${enabled_network_list}; do
433       if [[ "$network" != "admin_network" && "$network" != "public_network" ]]; then
434         continue
435       fi
436       this_interface=$(eval echo \${${network}_bridged_interface})
437       # check if this a bridged interface for this network
438       if [[ ! -z "$this_interface" || "$this_interface" != "none" ]]; then
439         if ! attach_interface_to_ovs ${NET_MAP[$network]} ${this_interface} ${network}; then
440           echo -e "${red}ERROR: Unable to bridge interface ${this_interface} to bridge ${NET_MAP[$network]} for enabled network: ${network}${reset}"
441           exit 1
442         else
443           echo -e "${blue}INFO: Interface ${this_interface} bridged to bridge ${NET_MAP[$network]} for enabled network: ${network}${reset}"
444         fi
445       else
446         echo "${red}ERROR: Unable to determine interface to bridge to for enabled network: ${network}${reset}"
447         exit 1
448       fi
449     done
450   fi
451
452   # ensure storage pool exists and is started
453   virsh pool-list --all | grep default > /dev/null || virsh pool-define-as --name default dir --target /var/lib/libvirt/images
454   virsh pool-list | grep -Eo "default\s+active" > /dev/null || (virsh pool-autostart default; virsh pool-start default)
455
456   if ! egrep '^flags.*(vmx|svm)' /proc/cpuinfo > /dev/null; then
457     echo "${red}virtualization extensions not found, kvm kernel module insertion may fail.\n  \
458 Are you sure you have enabled vmx in your bios or hypervisor?${reset}"
459   fi
460
461   if ! lsmod | grep kvm > /dev/null; then modprobe kvm; fi
462   if ! lsmod | grep kvm_intel > /dev/null; then modprobe kvm_intel; fi
463
464   if ! lsmod | grep kvm > /dev/null; then
465     echo "${red}kvm kernel modules not loaded!${reset}"
466     return 1
467   fi
468
469   ##sshkeygen for root
470   if [ ! -e ~/.ssh/id_rsa.pub ]; then
471     ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
472   fi
473
474   echo "${blue}All dependencies installed and running${reset}"
475 }
476
477 ##verify vm exists, an has a dhcp lease assigned to it
478 ##params: none
479 function setup_undercloud_vm {
480   if ! virsh list --all | grep undercloud > /dev/null; then
481       undercloud_nets="default admin_network"
482       if [[ $enabled_network_list =~ "public_network" ]]; then
483         undercloud_nets+=" public_network"
484       fi
485       define_vm undercloud hd 30 "$undercloud_nets"
486
487       ### this doesn't work for some reason I was getting hangup events so using cp instead
488       #virsh vol-upload --pool default --vol undercloud.qcow2 --file $CONFIG/stack/undercloud.qcow2
489       #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)
490       #2015-12-05 12:57:20.569+0000: 8755: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds
491       #2015-12-05 12:57:20.569+0000: 8756: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds
492       #error: cannot close volume undercloud.qcow2
493       #error: internal error: received hangup / error event on socket
494       #error: Reconnected to the hypervisor
495
496       local undercloud_dst=/var/lib/libvirt/images/undercloud.qcow2
497       cp -f $RESOURCES/undercloud.qcow2 $undercloud_dst
498
499       # resize Undercloud machine
500       echo "Checking if Undercloud needs to be resized..."
501       undercloud_size=$(LIBGUESTFS_BACKEND=direct virt-filesystems --long -h --all -a $undercloud_dst |grep device | grep -Eo "[0-9\.]+G" | sed -n 's/\([0-9][0-9]*\).*/\1/p')
502       if [ "$undercloud_size" -lt 30 ]; then
503         qemu-img resize /var/lib/libvirt/images/undercloud.qcow2 +25G
504         LIBGUESTFS_BACKEND=direct virt-resize --expand /dev/sda1 $RESOURCES/undercloud.qcow2 $undercloud_dst
505         LIBGUESTFS_BACKEND=direct virt-customize -a $undercloud_dst --run-command 'xfs_growfs -d /dev/sda1 || true'
506         new_size=$(LIBGUESTFS_BACKEND=direct virt-filesystems --long -h --all -a $undercloud_dst |grep filesystem | grep -Eo "[0-9\.]+G" | sed -n 's/\([0-9][0-9]*\).*/\1/p')
507         if [ "$new_size" -lt 30 ]; then
508           echo "Error resizing Undercloud machine, disk size is ${new_size}"
509           exit 1
510         else
511           echo "Undercloud successfully resized"
512         fi
513       else
514         echo "Skipped Undercloud resize, upstream is large enough"
515       fi
516
517   else
518       echo "Found Undercloud VM, using existing VM"
519   fi
520
521   # if the VM is not running update the authkeys and start it
522   if ! virsh list | grep undercloud > /dev/null; then
523     echo "Injecting ssh key to Undercloud VM"
524     LIBGUESTFS_BACKEND=direct virt-customize -a $undercloud_dst --run-command "mkdir -p /root/.ssh/" \
525         --upload ~/.ssh/id_rsa.pub:/root/.ssh/authorized_keys \
526         --run-command "chmod 600 /root/.ssh/authorized_keys && restorecon /root/.ssh/authorized_keys" \
527         --run-command "cp /root/.ssh/authorized_keys /home/stack/.ssh/" \
528         --run-command "chown stack:stack /home/stack/.ssh/authorized_keys && chmod 600 /home/stack/.ssh/authorized_keys"
529     virsh start undercloud
530   fi
531
532   sleep 10 # let undercloud get started up
533
534   # get the undercloud VM IP
535   CNT=10
536   echo -n "${blue}Waiting for Undercloud's dhcp address${reset}"
537   undercloud_mac=$(virsh domiflist undercloud | grep default | awk '{ print $5 }')
538   while ! $(arp -e | grep ${undercloud_mac} > /dev/null) && [ $CNT -gt 0 ]; do
539       echo -n "."
540       sleep 10
541       CNT=$((CNT-1))
542   done
543   UNDERCLOUD=$(arp -e | grep ${undercloud_mac} | awk {'print $1'})
544
545   if [ -z "$UNDERCLOUD" ]; then
546     echo "\n\nCan't get IP for Undercloud. Can Not Continue."
547     exit 1
548   else
549      echo -e "${blue}\rUndercloud VM has IP $UNDERCLOUD${reset}"
550   fi
551
552   CNT=10
553   echo -en "${blue}\rValidating Undercloud VM connectivity${reset}"
554   while ! ping -c 1 $UNDERCLOUD > /dev/null && [ $CNT -gt 0 ]; do
555       echo -n "."
556       sleep 3
557       CNT=$((CNT-1))
558   done
559   if [ "$CNT" -eq 0 ]; then
560       echo "Failed to contact Undercloud. Can Not Continue"
561       exit 1
562   fi
563   CNT=10
564   while ! ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "echo ''" 2>&1> /dev/null && [ $CNT -gt 0 ]; do
565       echo -n "."
566       sleep 3
567       CNT=$((CNT-1))
568   done
569   if [ "$CNT" -eq 0 ]; then
570       echo "Failed to connect to Undercloud. Can Not Continue"
571       exit 1
572   fi
573
574   # extra space to overwrite the previous connectivity output
575   echo -e "${blue}\r                                                                 ${reset}"
576   sleep 1
577   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"
578
579   # ssh key fix for stack user
580   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "restorecon -r /home/stack"
581 }
582
583 ##Create virtual nodes in virsh
584 ##params: none
585 function setup_virtual_baremetal {
586   #start by generating the opening json for instackenv.json
587   cat > $CONFIG/instackenv-virt.json << EOF
588 {
589   "nodes": [
590 EOF
591
592   # next create the virtual machines and add their definitions to the file
593   for i in $(seq 0 $vm_index); do
594     if ! virsh list --all | grep baremetal${i} > /dev/null; then
595       define_vm baremetal${i} network 41 'admin_network'
596       for n in private_network public_network storage_network; do
597         if [[ $enabled_network_list =~ $n ]]; then
598           echo -n "$n "
599           virsh attach-interface --domain baremetal${i} --type network --source $n --model rtl8139 --config
600         fi
601       done
602     else
603       echo "Found Baremetal ${i} VM, using existing VM"
604     fi
605     #virsh vol-list default | grep baremetal${i} 2>&1> /dev/null || virsh vol-create-as default baremetal${i}.qcow2 41G --format qcow2
606     mac=$(virsh domiflist baremetal${i} | grep admin_network | awk '{ print $5 }')
607
608     cat >> $CONFIG/instackenv-virt.json << EOF
609     {
610       "pm_addr": "192.168.122.1",
611       "pm_user": "root",
612       "pm_password": "INSERT_STACK_USER_PRIV_KEY",
613       "pm_type": "pxe_ssh",
614       "mac": [
615         "$mac"
616       ],
617       "cpu": "2",
618       "memory": "8192",
619       "disk": "41",
620       "arch": "x86_64"
621     },
622 EOF
623   done
624
625   #truncate the last line to remove the comma behind the bracket
626   tail -n 1 $CONFIG/instackenv-virt.json | wc -c | xargs -I {} truncate $CONFIG/instackenv-virt.json -s -{}
627
628   #finally reclose the bracket and close the instackenv.json file
629   cat >> $CONFIG/instackenv-virt.json << EOF
630     }
631   ],
632   "arch": "x86_64",
633   "host-ip": "192.168.122.1",
634   "power_manager": "nova.virt.baremetal.virtual_power_driver.VirtualPowerManager",
635   "seed-ip": "",
636   "ssh-key": "INSERT_STACK_USER_PRIV_KEY",
637   "ssh-user": "root"
638 }
639 EOF
640 }
641
642 ##Create virtual nodes in virsh
643 ##params: name - String: libvirt name for VM
644 ##        bootdev - String: boot device for the VM
645 ##        disksize - Number: size of the disk in GB
646 ##        ovs_bridges: - List: list of ovs bridges
647 function define_vm () {
648   # Create the libvirt storage volume
649   if virsh vol-list default | grep ${1}.qcow2 2>&1> /dev/null; then
650     volume_path=$(virsh vol-path --pool default ${1}.qcow2 || echo "/var/lib/libvirt/images/${1}.qcow2")
651     echo "Volume ${1} exists. Deleting Existing Volume $volume_path"
652     virsh vol-dumpxml ${1}.qcow2 --pool default > /dev/null || echo '' #ok for this to fail
653     touch $volume_path
654     virsh vol-delete ${1}.qcow2 --pool default
655   fi
656   virsh vol-create-as default ${1}.qcow2 ${3}G --format qcow2
657   volume_path=$(virsh vol-path --pool default ${1}.qcow2)
658   if [ ! -f $volume_path ]; then
659       echo "$volume_path Not created successfully... Aborting"
660       exit 1
661   fi
662
663   # create the VM
664   /usr/libexec/openstack-tripleo/configure-vm --name $1 \
665                                               --bootdev $2 \
666                                               --image "$volume_path" \
667                                               --diskbus sata \
668                                               --arch x86_64 \
669                                               --cpus 2 \
670                                               --memory 8388608 \
671                                               --libvirt-nic-driver virtio \
672                                               --baremetal-interface $4
673 }
674
675 ##Set network-environment settings
676 ##params: network-environment file to edit
677 function configure_network_environment {
678   local tht_dir
679   tht_dir=/usr/share/openstack-tripleo-heat-templates/network
680
681   sed -i '/ControlPlaneSubnetCidr/c\\  ControlPlaneSubnetCidr: "'${admin_network_cidr##*/}'"' $1
682   sed -i '/ControlPlaneDefaultRoute/c\\  ControlPlaneDefaultRoute: '${admin_network_provisioner_ip}'' $1
683   sed -i '/ExternalNetCidr/c\\  ExternalNetCidr: '${public_network_cidr}'' $1
684   sed -i "/ExternalAllocationPools/c\\  ExternalAllocationPools: [{'start': '${public_network_usable_ip_range%%,*}', 'end': '${public_network_usable_ip_range##*,}'}]" $1
685   sed -i '/ExternalInterfaceDefaultRoute/c\\  ExternalInterfaceDefaultRoute: '${public_network_gateway}'' $1
686   sed -i '/EC2MetadataIp/c\\  EC2MetadataIp: '${admin_network_provisioner_ip}'' $1
687
688   # check for private network
689   if [[ ! -z "$private_network_enabled" && "$private_network_enabled" == "true" ]]; then
690       sed -i 's#^.*Network::Tenant.*$#  OS::TripleO::Network::Tenant: '${tht_dir}'/tenant.yaml#' $1
691       sed -i 's#^.*Controller::Ports::TenantPort:.*$#  OS::TripleO::Controller::Ports::TenantPort: '${tht_dir}'/ports/tenant.yaml#' $1
692       sed -i 's#^.*Compute::Ports::TenantPort:.*$#  OS::TripleO::Compute::Ports::TenantPort: '${tht_dir}'/ports/tenant.yaml#' $1
693       sed -i "/TenantAllocationPools/c\\  TenantAllocationPools: [{'start': '${private_network_usable_ip_range%%,*}', 'end': '${private_network_usable_ip_range##*,}'}]" $1
694       sed -i '/TenantNetCidr/c\\  TenantNetCidr: '${private_network_cidr}'' $1
695       nics_cfg+=_private
696   else
697       sed -i 's#^.*Network::Tenant.*$#  OS::TripleO::Network::Tenant: '${tht_dir}'/noop.yaml#' $1
698       sed -i 's#^.*Controller::Ports::TenantPort:.*$#  OS::TripleO::Controller::Ports::TenantPort: '${tht_dir}'/ports/noop.yaml#' $1
699       sed -i 's#^.*Compute::Ports::TenantPort:.*$#  OS::TripleO::Compute::Ports::TenantPort: '${tht_dir}'/ports/noop.yaml#' $1
700   fi
701
702   # check for storage network
703   if [[ ! -z "$storage_network_enabled" && "$storage_network_enabled" == "true" ]]; then
704       sed -i 's#^.*Network::Storage:.*$#  OS::TripleO::Network::Storage: '${tht_dir}'/storage.yaml#' $1
705       sed -i 's#^.*Network::Ports::StorageVipPort:.*$#  OS::TripleO::Network::Ports::StorageVipPort: '${tht_dir}'/ports/storage.yaml#' $1
706       sed -i 's#^.*Controller::Ports::StoragePort:.*$#  OS::TripleO::Controller::Ports::StoragePort: '${tht_dir}'/ports/storage.yaml#' $1
707       sed -i 's#^.*Compute::Ports::StoragePort:.*$#  OS::TripleO::Compute::Ports::StoragePort: '${tht_dir}'/ports/storage.yaml#' $1
708       sed -i "/StorageAllocationPools/c\\  StorageAllocationPools: [{'start': '${storage_network_usable_ip_range%%,*}', 'end': '${storage_network_usable_ip_range##*,}'}]" $1
709       sed -i '/StorageNetCidr/c\\  StorageNetCidr: '${storage_network_cidr}'' $1
710       nics_cfg+=_storage
711   else
712       sed -i 's#^.*Network::Storage:.*$#  OS::TripleO::Network::Storage: '${tht_dir}'/noop.yaml#' $1
713       sed -i 's#^.*Network::Ports::StorageVipPort:.*$#  OS::TripleO::Network::Ports::StorageVipPort: '${tht_dir}'/ports/noop.yaml#' $1
714       sed -i 's#^.*Controller::Ports::StoragePort:.*$#  OS::TripleO::Controller::Ports::StoragePort: '${tht_dir}'/ports/noop.yaml#' $1
715       sed -i 's#^.*Compute::Ports::StoragePort:.*$#  OS::TripleO::Compute::Ports::StoragePort: '${tht_dir}'/ports/noop.yaml#' $1
716   fi
717
718   # check for ODL L3
719   if [ "${deploy_options_array['sdn_l3']}" == 'true' ]; then
720       nics_cfg+=_br-ex_no-public-ip
721   fi
722
723 }
724 ##Copy over the glance images and instackenv json file
725 ##params: none
726 function configure_undercloud {
727
728   echo
729   echo "Copying configuration files to Undercloud"
730   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
731     configure_network_environment $CONFIG/network-environment.yaml
732     echo -e "${blue}Network Environment set for Deployment: ${reset}"
733     cat $CONFIG/network-environment.yaml
734     scp ${SSH_OPTIONS[@]} $CONFIG/network-environment.yaml "stack@$UNDERCLOUD":
735     ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI
736 mkdir nics/
737 cat > nics/controller.yaml << EOF
738 $(nics_cfg=$nics_cfg sh $CONFIG/nics-controller.yaml.template)
739 EOF
740 cat > nics/compute.yaml << EOF
741 $(nics_cfg=$nics_cfg sh $CONFIG/nics-compute.yaml.template)
742 EOF
743 EOI
744   fi
745
746   # ensure stack user on Undercloud machine has an ssh key
747   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "if [ ! -e ~/.ssh/id_rsa.pub ]; then ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa; fi"
748
749   if [ "$virtual" == "TRUE" ]; then
750
751       # copy the Undercloud VM's stack user's pub key to
752       # root's auth keys so that Undercloud can control
753       # vm power on the hypervisor
754       ssh ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "cat /home/stack/.ssh/id_rsa.pub" >> /root/.ssh/authorized_keys
755
756       DEPLOY_OPTIONS+=" --libvirt-type qemu"
757       INSTACKENV=$CONFIG/instackenv-virt.json
758
759       # upload instackenv file to Undercloud for virtual deployment
760       scp ${SSH_OPTIONS[@]} $INSTACKENV "stack@$UNDERCLOUD":instackenv.json
761   fi
762
763   # allow stack to control power management on the hypervisor via sshkey
764   # only if this is a virtual deployment
765   if [ "$virtual" == "TRUE" ]; then
766       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
767 while read -r line; do
768   stack_key=\${stack_key}\\\\\\\\n\${line}
769 done < <(cat ~/.ssh/id_rsa)
770 stack_key=\$(echo \$stack_key | sed 's/\\\\\\\\n//')
771 sed -i 's~INSERT_STACK_USER_PRIV_KEY~'"\$stack_key"'~' instackenv.json
772 EOI
773   fi
774
775   # copy stack's ssh key to this users authorized keys
776   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "cat /home/stack/.ssh/id_rsa.pub" >> ~/.ssh/authorized_keys
777
778   # disable requiretty for sudo
779   ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "sed -i 's/Defaults\s*requiretty//'" /etc/sudoers
780
781   # configure undercloud on Undercloud VM
782   echo "Running undercloud configuration."
783   echo "Logging undercloud configuration to undercloud:/home/stack/apex-undercloud-install.log"
784   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI
785 if [[ "$net_isolation_enabled" == "TRUE" ]]; then
786   sed -i 's/#local_ip/local_ip/' undercloud.conf
787   sed -i 's/#network_gateway/network_gateway/' undercloud.conf
788   sed -i 's/#network_cidr/network_cidr/' undercloud.conf
789   sed -i 's/#dhcp_start/dhcp_start/' undercloud.conf
790   sed -i 's/#dhcp_end/dhcp_end/' undercloud.conf
791   sed -i 's/#inspection_iprange/inspection_iprange/' undercloud.conf
792   sed -i 's/#undercloud_debug/undercloud_debug/' undercloud.conf
793
794   openstack-config --set undercloud.conf DEFAULT local_ip ${admin_network_provisioner_ip}/${admin_network_cidr##*/}
795   openstack-config --set undercloud.conf DEFAULT network_gateway ${admin_network_provisioner_ip}
796   openstack-config --set undercloud.conf DEFAULT network_cidr ${admin_network_cidr}
797   openstack-config --set undercloud.conf DEFAULT dhcp_start ${admin_network_dhcp_range%%,*}
798   openstack-config --set undercloud.conf DEFAULT dhcp_end ${admin_network_dhcp_range##*,}
799   openstack-config --set undercloud.conf DEFAULT inspection_iprange ${admin_network_introspection_range}
800   openstack-config --set undercloud.conf DEFAULT undercloud_debug false
801
802 fi
803
804 sudo sed -i '/CephClusterFSID:/c\\  CephClusterFSID: \\x27$(cat /proc/sys/kernel/random/uuid)\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml
805 sudo sed -i '/CephMonKey:/c\\  CephMonKey: \\x27'"\$(ceph-authtool --gen-print-key)"'\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml
806 sudo sed -i '/CephAdminKey:/c\\  CephAdminKey: \\x27'"\$(ceph-authtool --gen-print-key)"'\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml
807
808 # we assume that packages will not need to be updated with undercloud install
809 # and that it will be used only to configure the undercloud
810 # packages updates would need to be handled manually with yum update
811 sudo cp -f /usr/share/diskimage-builder/elements/yum/bin/install-packages /usr/share/diskimage-builder/elements/yum/bin/install-packages.bak
812 cat << 'EOF' | sudo tee /usr/share/diskimage-builder/elements/yum/bin/install-packages > /dev/null
813 #!/bin/sh
814 exit 0
815 EOF
816
817 openstack undercloud install &> apex-undercloud-install.log || {
818     # cat the undercloud install log incase it fails
819     echo "ERROR: openstack undercloud install has failed. Dumping Log:"
820     cat apex-undercloud-install.log
821     exit 1
822 }
823
824 sleep 30
825 sudo systemctl restart openstack-glance-api
826 sudo systemctl restart openstack-nova-conductor
827 sudo systemctl restart openstack-nova-compute
828 EOI
829 # WORKAROUND: must restart the above services to fix sync problem with nova compute manager
830 # TODO: revisit and file a bug if necessary. This should eventually be removed
831 # as well as glance api problem
832 echo -e "${blue}INFO: Sleeping 15 seconds while services come back from restart${reset}"
833 sleep 15
834
835 }
836
837 ##preping it for deployment and launch the deploy
838 ##params: none
839 function undercloud_prep_overcloud_deploy {
840   if [[ "${#deploy_options_array[@]}" -eq 0 || "${deploy_options_array['sdn_controller']}" == 'opendaylight' ]]; then
841     if [ "${deploy_options_array['sdn_l3']}" == 'true' ]; then
842       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight_l3.yaml"
843     elif [ "${deploy_options_array['sfc']}" == 'true' ]; then
844       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight_sfc.yaml"
845     elif [ "${deploy_options_array['vpn']}" == 'true' ]; then
846       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight_sdnvpn.yaml"
847     else
848       DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight.yaml"
849     fi
850     SDN_IMAGE=opendaylight
851     if [ "${deploy_options_array['sfc']}" == 'true' ]; then
852       SDN_IMAGE+=-sfc
853       if [ ! -f $RESOURCES/overcloud-full-${SDN_IMAGE}.qcow2 ]; then
854           echo "${red} $RESOURCES/overcloud-full-${SDN_IMAGE}.qcow2 is required to execute an SFC deployment."
855           echo "Please install the opnfv-apex-opendaylight-sfc package to provide this overcloud image for deployment.${reset}"
856           exit 1
857       fi
858     fi
859   elif [ "${deploy_options_array['sdn_controller']}" == 'opendaylight-external' ]; then
860     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight-external.yaml"
861     SDN_IMAGE=opendaylight
862   elif [ "${deploy_options_array['sdn_controller']}" == 'onos' ]; then
863     DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/onos.yaml"
864     SDN_IMAGE=onos
865   elif [ "${deploy_options_array['sdn_controller']}" == 'opencontrail' ]; then
866     echo -e "${red}ERROR: OpenContrail is currently unsupported...exiting${reset}"
867     exit 1
868   elif [[ -z "${deploy_options_array['sdn_controller']}" || "${deploy_options_array['sdn_controller']}" == 'false' ]]; then
869     echo -e "${blue}INFO: SDN Controller disabled...will deploy nosdn scenario${reset}"
870     SDN_IMAGE=opendaylight
871   else
872     echo "${red}Invalid sdn_controller: ${deploy_options_array['sdn_controller']}${reset}"
873     echo "${red}Valid choices are opendaylight, opendaylight-external, onos, opencontrail, false, or null${reset}"
874     exit 1
875   fi
876
877   # Make sure the correct overcloud image is available
878   if [ ! -f $RESOURCES/overcloud-full-${SDN_IMAGE}.qcow2 ]; then
879       echo "${red} $RESOURCES/overcloud-full-${SDN_IMAGE}.qcow2 is required to execute your deployment."
880       echo "Both ONOS and OpenDaylight are currently deployed from this image."
881       echo "Please install the opnfv-apex package to provide this overcloud image for deployment.${reset}"
882       exit 1
883   fi
884
885   echo "Copying overcloud image to Undercloud"
886   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "rm -f overcloud-full.qcow2"
887   scp ${SSH_OPTIONS[@]} $RESOURCES/overcloud-full-${SDN_IMAGE}.qcow2 "stack@$UNDERCLOUD":overcloud-full.qcow2
888
889   # make sure ceph is installed
890   DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml"
891
892   # scale compute nodes according to inventory
893   total_nodes=$(ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "cat /home/stack/instackenv.json | grep -c memory")
894
895   # check if HA is enabled
896   if [[ "$ha_enabled" == "TRUE" || "$ha_enabled" == "true" ]]; then
897      DEPLOY_OPTIONS+=" --control-scale 3"
898      compute_nodes=$((total_nodes - 3))
899      DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/puppet-pacemaker.yaml"
900   else
901      compute_nodes=$((total_nodes - 1))
902   fi
903
904   if [ "$compute_nodes" -le 0 ]; then
905     echo -e "${red}ERROR: Invalid number of compute nodes: ${compute_nodes}. Check your inventory file.${reset}"
906     exit 1
907   else
908     echo -e "${blue}INFO: Number of compute nodes set for deployment: ${compute_nodes}${reset}"
909     DEPLOY_OPTIONS+=" --compute-scale ${compute_nodes}"
910   fi
911
912   if [[ "$net_isolation_enabled" == "TRUE" ]]; then
913      #DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/network-isolation.yaml"
914      DEPLOY_OPTIONS+=" -e network-environment.yaml"
915   fi
916
917   if [[ "$ha_enabled" == "TRUE" || "$ha_enabled" == "true"  ]] || [[ "$net_isolation_enabled" == "TRUE" ]]; then
918      DEPLOY_OPTIONS+=" --ntp-server $ntp_server"
919   fi
920
921   if [[ ! "$virtual" == "TRUE" ]]; then
922      DEPLOY_OPTIONS+=" --control-flavor control --compute-flavor compute"
923   fi
924
925   echo -e "${blue}INFO: Deploy options set:\n${DEPLOY_OPTIONS}${reset}"
926
927   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
928 if [ "$debug" == 'TRUE' ]; then
929     LIBGUESTFS_BACKEND=direct virt-customize -a overcloud-full.qcow2 --root-password password:opnfvapex
930 fi
931
932 source stackrc
933 set -o errexit
934 echo "Uploading overcloud glance images"
935 openstack overcloud image upload
936 echo "Configuring undercloud and discovering nodes"
937 openstack baremetal import --json instackenv.json
938 openstack baremetal configure boot
939 #if [[ -z "$virtual" ]]; then
940 #  openstack baremetal introspection bulk start
941 #fi
942 echo "Configuring flavors"
943 for flavor in baremetal control compute; do
944   echo -e "${blue}INFO: Updating flavor: \${flavor}${reset}"
945   if openstack flavor list | grep \${flavor}; then
946     openstack flavor delete \${flavor}
947   fi
948   openstack flavor create --id auto --ram 4096 --disk 39 --vcpus 1 \${flavor}
949   if ! openstack flavor list | grep \${flavor}; then
950     echo -e "${red}ERROR: Unable to create flavor \${flavor}${reset}"
951   fi
952 done
953 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" baremetal
954 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="control" control
955 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="compute" compute
956 echo "Configuring nameserver on ctlplane network"
957 neutron subnet-update \$(neutron subnet-list | grep -v id | grep -v \\\\-\\\\- | awk {'print \$2'}) --dns-nameserver 8.8.8.8
958 echo "Executing overcloud deployment, this should run for an extended period without output."
959 sleep 60 #wait for Hypervisor stats to check-in to nova
960 # save deploy command so it can be used for debugging
961 cat > deploy_command << EOF
962 openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90
963 EOF
964 EOI
965
966   if [ "$interactive" == "TRUE" ]; then
967     if ! prompt_user "Overcloud Deployment"; then
968       echo -e "${blue}INFO: User requests exit${reset}"
969       exit 0
970     fi
971   fi
972
973   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
974 source stackrc
975 set -o errexit
976 openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90
977 EOI
978
979   if [ "$debug" == 'TRUE' ]; then
980       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
981 source overcloudrc
982 echo "Keystone Endpoint List:"
983 keystone endpoint-list
984 echo "Keystone Service List"
985 keystone service-list
986 cinder quota-show \$(openstack project list | grep admin | awk {'print \$2'})
987 EOI
988   fi
989 }
990
991 ##Post configuration after install
992 ##params: none
993 function configure_post_install {
994   local opnfv_attach_networks ovs_ip ip_range net_cidr tmp_ip
995   opnfv_attach_networks="admin_network public_network"
996
997   echo -e "${blue}INFO: Post Install Configuration Running...${reset}"
998
999   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
1000 source overcloudrc
1001 set -o errexit
1002 echo "Configuring Neutron external network"
1003 neutron net-create external --router:external=True --tenant-id \$(keystone tenant-get service | grep id | awk '{ print \$4 }')
1004 neutron subnet-create --name external-net --tenant-id \$(keystone tenant-get service | grep id | awk '{ print \$4 }') --disable-dhcp external --gateway ${public_network_gateway} --allocation-pool start=${public_network_floating_ip_range%%,*},end=${public_network_floating_ip_range##*,} ${public_network_cidr}
1005 EOI
1006
1007   echo -e "${blue}INFO: Checking if OVS bridges have IP addresses...${reset}"
1008   for network in ${opnfv_attach_networks}; do
1009     ovs_ip=$(find_ip ${NET_MAP[$network]})
1010     tmp_ip=''
1011     if [ -n "$ovs_ip" ]; then
1012       echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} has IP address ${ovs_ip}${reset}"
1013     else
1014       echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} missing IP, will configure${reset}"
1015       # use last IP of allocation pool
1016       eval "ip_range=\${${network}_usable_ip_range}"
1017       ovs_ip=${ip_range##*,}
1018       eval "net_cidr=\${${network}_cidr}"
1019       sudo ip addr add ${ovs_ip}/${net_cidr##*/} dev ${NET_MAP[$network]}
1020       sudo ip link set up ${NET_MAP[$network]}
1021       tmp_ip=$(find_ip ${NET_MAP[$network]})
1022       if [ -n "$tmp_ip" ]; then
1023         echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} IP set: ${tmp_ip}${reset}"
1024         continue
1025       else
1026         echo -e "${red}ERROR: Unable to set OVS Bridge ${NET_MAP[$network]} with IP: ${ovs_ip}${reset}"
1027         return 1
1028       fi
1029     fi
1030   done
1031
1032   # for virtual, we NAT public network through Undercloud
1033   if [ "$virtual" == "TRUE" ]; then
1034     if ! configure_undercloud_nat ${public_network_cidr}; then
1035       echo -e "${red}ERROR: Unable to NAT undercloud with external net: ${public_network_cidr}${reset}"
1036       exit 1
1037     else
1038       echo -e "${blue}INFO: Undercloud VM has been setup to NAT Overcloud public network${reset}"
1039     fi
1040   fi
1041
1042   # for sfc deployments we need the vxlan workaround
1043   if [ "${deploy_options_array['sfc']}" == 'true' ]; then
1044       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
1045 source stackrc
1046 set -o errexit
1047 for node in \$(nova list | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"); do
1048 ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
1049 sudo ifconfig br-int up
1050 sudo ip route add 123.123.123.0/24 dev br-int
1051 EOF
1052 done
1053 EOI
1054   fi
1055
1056   # Collect deployment logs
1057   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
1058 mkdir -p ~/deploy_logs
1059 rm -rf deploy_logs/*
1060 source stackrc
1061 set -o errexit
1062 for node in \$(nova list | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"); do
1063  ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
1064  sudo cp /var/log/messages /home/heat-admin/messages.log
1065  sudo chown heat-admin /home/heat-admin/messages.log
1066 EOF
1067 scp ${SSH_OPTIONS[@]} heat-admin@\$node:/home/heat-admin/messages.log ~/deploy_logs/\$node.messages.log
1068 if [ "$debug" == "TRUE" ]; then
1069     nova list --ip \$node
1070     echo "---------------------------"
1071     echo "-----/var/log/messages-----"
1072     echo "---------------------------"
1073     cat ~/deploy_logs/\$node.messages.log
1074     echo "---------------------------"
1075     echo "----------END LOG----------"
1076     echo "---------------------------"
1077 fi
1078  ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
1079  sudo rm -f /home/heat-admin/messages.log
1080 EOF
1081 done
1082
1083 # Print out the dashboard URL
1084 source stackrc
1085 echo "Overcloud dashboard available at http://\$(heat output-show overcloud PublicVip | sed 's/"//g')/dashboard"
1086 EOI
1087
1088 }
1089
1090 display_usage() {
1091   echo -e "Usage:\n$0 [arguments] \n"
1092   echo -e "   -c|--config : Directory to configuration files. Optional.  Defaults to /var/opt/opnfv/ \n"
1093   echo -e "   -d|--deploy-settings : Full path to deploy settings yaml file. Optional.  Defaults to null \n"
1094   echo -e "   -i|--inventory : Full path to inventory yaml file. Required only for baremetal \n"
1095   echo -e "   -n|--net-settings : Full path to network settings file. Optional. \n"
1096   echo -e "   -p|--ping-site : site to use to verify IP connectivity. Optional. Defaults to 8.8.8.8 \n"
1097   echo -e "   -r|--resources : Directory to deployment resources. Optional.  Defaults to /var/opt/opnfv/stack \n"
1098   echo -e "   -v|--virtual : Virtualize overcloud nodes instead of using baremetal. \n"
1099   echo -e "   --no-ha : disable High Availability deployment scheme, this assumes a single controller and single compute node \n"
1100   echo -e "   --flat : disable Network Isolation and use a single flat network for the underlay network.\n"
1101   echo -e "   --no-post-config : disable Post Install configuration."
1102   echo -e "   --debug : enable debug output."
1103   echo -e "   --interactive : enable interactive deployment mode which requires user to confirm steps of deployment."
1104 }
1105
1106 ##translates the command line parameters into variables
1107 ##params: $@ the entire command line is passed
1108 ##usage: parse_cmd_line() "$@"
1109 parse_cmdline() {
1110   echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n"
1111   echo "Use -h to display help"
1112   sleep 2
1113
1114   while [ "${1:0:1}" = "-" ]
1115   do
1116     case "$1" in
1117         -h|--help)
1118                 display_usage
1119                 exit 0
1120             ;;
1121         -c|--config)
1122                 CONFIG=$2
1123                 echo "Deployment Configuration Directory Overridden to: $2"
1124                 shift 2
1125             ;;
1126         -d|--deploy-settings)
1127                 DEPLOY_SETTINGS_FILE=$2
1128                 echo "Deployment Configuration file: $2"
1129                 shift 2
1130             ;;
1131         -i|--inventory)
1132                 INVENTORY_FILE=$2
1133                 shift 2
1134             ;;
1135         -n|--net-settings)
1136                 NETSETS=$2
1137                 echo "Network Settings Configuration file: $2"
1138                 shift 2
1139             ;;
1140         -p|--ping-site)
1141                 ping_site=$2
1142                 echo "Using $2 as the ping site"
1143                 shift 2
1144             ;;
1145         -r|--resources)
1146                 RESOURCES=$2
1147                 echo "Deployment Resources Directory Overridden to: $2"
1148                 shift 2
1149             ;;
1150         -v|--virtual)
1151                 virtual="TRUE"
1152                 echo "Executing a Virtual Deployment"
1153                 shift 1
1154             ;;
1155         --no-ha )
1156                 ha_enabled="FALSE"
1157                 vm_index=1
1158                 echo "HA Deployment Disabled"
1159                 shift 1
1160             ;;
1161         --flat )
1162                 net_isolation_enabled="FALSE"
1163                 echo "Underlay Network Isolation Disabled: using flat configuration"
1164                 shift 1
1165             ;;
1166         --no-post-config )
1167                 post_config="FALSE"
1168                 echo "Post install configuration disabled"
1169                 shift 1
1170             ;;
1171         --debug )
1172                 debug="TRUE"
1173                 echo "Enable debug output"
1174                 shift 1
1175             ;;
1176         --interactive )
1177                 interactive="TRUE"
1178                 echo "Interactive mode enabled"
1179                 shift 1
1180             ;;
1181         *)
1182                 display_usage
1183                 exit 1
1184             ;;
1185     esac
1186   done
1187
1188   if [[ ! -z "$NETSETS" && "$net_isolation_enabled" == "FALSE" ]]; then
1189     echo -e "${red}INFO: Single flat network requested. Only admin_network settings will be used!${reset}"
1190   elif [[ -z "$NETSETS" ]]; then
1191     echo -e "${red}ERROR: You must provide a network_settings file with -n.${reset}"
1192     exit 1
1193   fi
1194
1195   if [[ -n "$virtual" && -n "$INVENTORY_FILE" ]]; then
1196     echo -e "${red}ERROR: You should not specify an inventory with virtual deployments${reset}"
1197     exit 1
1198   fi
1199
1200   if [[ -z "$DEPLOY_SETTINGS_FILE" || ! -f "$DEPLOY_SETTINGS_FILE" ]]; then
1201     echo -e "${red}ERROR: Deploy Settings: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
1202     exit 1
1203   fi
1204
1205   if [[ ! -z "$NETSETS" && ! -f "$NETSETS" ]]; then
1206     echo -e "${red}ERROR: Network Settings: ${NETSETS} does not exist! Exiting...${reset}"
1207     exit 1
1208   fi
1209
1210   if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
1211     echo -e "{$red}ERROR: Inventory File: ${INVENTORY_FILE} does not exist! Exiting...${reset}"
1212     exit 1
1213   fi
1214
1215   if [[ -z "$virtual" && -z "$INVENTORY_FILE" ]]; then
1216     echo -e "${red}ERROR: You must specify an inventory file for baremetal deployments! Exiting...${reset}"
1217     exit 1
1218   fi
1219
1220   if [[ "$net_isolation_enabled" == "FALSE" && "$post_config" == "TRUE" ]]; then
1221     echo -e "${blue}INFO: Post Install Configuration will be skipped.  It is not supported with --flat${reset}"
1222     post_config="FALSE"
1223   fi
1224
1225   ##LIBRARIES
1226   # Do this after cli parse so that $CONFIG is set properly
1227   source $CONFIG/lib/common-functions.sh
1228   source $CONFIG/lib/installer/onos/onos_gw_mac_update.sh
1229
1230 }
1231
1232 ##END FUNCTIONS
1233
1234 main() {
1235   parse_cmdline "$@"
1236   echo -e "${blue}INFO: Parsing network settings file...${reset}"
1237   parse_network_settings
1238   if ! configure_deps; then
1239     echo -e "${red}Dependency Validation Failed, Exiting.${reset}"
1240     exit 1
1241   fi
1242   if [ -n "$DEPLOY_SETTINGS_FILE" ]; then
1243     parse_deploy_settings
1244   fi
1245   setup_undercloud_vm
1246   if [ "$virtual" == "TRUE" ]; then
1247     setup_virtual_baremetal
1248   elif [ -n "$INVENTORY_FILE" ]; then
1249     parse_inventory_file
1250   fi
1251   configure_undercloud
1252   undercloud_prep_overcloud_deploy
1253   if [ "$post_config" == "TRUE" ]; then
1254     if ! configure_post_install; then
1255       echo -e "${red}ERROR:Post Install Configuration Failed, Exiting.${reset}"
1256       exit 1
1257     else
1258       echo -e "${blue}INFO: Post Install Configuration Complete${reset}"
1259     fi
1260   fi
1261   if [[ "${deploy_options_array['sdn_controller']}" == 'onos' ]]; then
1262     if ! onos_update_gw_mac ${public_network_cidr} ${public_network_gateway}; then
1263       echo -e "${red}ERROR:ONOS Post Install Configuration Failed, Exiting.${reset}"
1264       exit 1
1265     else
1266       echo -e "${blue}INFO: ONOS Post Install Configuration Complete${reset}"
1267     fi
1268   fi
1269 }
1270
1271 main "$@"