Merge "Documentation updates for Colorado"
[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   DEPLOY_OPTIONS+=" -e opnfv-environment.yaml"
926
927   echo -e "${blue}INFO: Deploy options set:\n${DEPLOY_OPTIONS}${reset}"
928
929   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
930 if [ "$debug" == 'TRUE' ]; then
931     LIBGUESTFS_BACKEND=direct virt-customize -a overcloud-full.qcow2 --root-password password:opnfvapex
932 fi
933
934 source stackrc
935 set -o errexit
936 echo "Uploading overcloud glance images"
937 openstack overcloud image upload
938 echo "Configuring undercloud and discovering nodes"
939 openstack baremetal import --json instackenv.json
940 openstack baremetal configure boot
941 #if [[ -z "$virtual" ]]; then
942 #  openstack baremetal introspection bulk start
943 #fi
944 echo "Configuring flavors"
945 for flavor in baremetal control compute; do
946   echo -e "${blue}INFO: Updating flavor: \${flavor}${reset}"
947   if openstack flavor list | grep \${flavor}; then
948     openstack flavor delete \${flavor}
949   fi
950   openstack flavor create --id auto --ram 4096 --disk 39 --vcpus 1 \${flavor}
951   if ! openstack flavor list | grep \${flavor}; then
952     echo -e "${red}ERROR: Unable to create flavor \${flavor}${reset}"
953   fi
954 done
955 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" baremetal
956 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="control" control
957 openstack flavor set --property "cpu_arch"="x86_64" --property "capabilities:boot_option"="local" --property "capabilities:profile"="compute" compute
958 echo "Configuring nameserver on ctlplane network"
959 neutron subnet-update \$(neutron subnet-list | grep -v id | grep -v \\\\-\\\\- | awk {'print \$2'}) --dns-nameserver 8.8.8.8
960 echo "Executing overcloud deployment, this should run for an extended period without output."
961 sleep 60 #wait for Hypervisor stats to check-in to nova
962 # save deploy command so it can be used for debugging
963 cat > deploy_command << EOF
964 openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90
965 EOF
966 EOI
967
968   if [ "$interactive" == "TRUE" ]; then
969     if ! prompt_user "Overcloud Deployment"; then
970       echo -e "${blue}INFO: User requests exit${reset}"
971       exit 0
972     fi
973   fi
974
975   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
976 source stackrc
977 set -o errexit
978 openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90
979 EOI
980
981   if [ "$debug" == 'TRUE' ]; then
982       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
983 source overcloudrc
984 echo "Keystone Endpoint List:"
985 keystone endpoint-list
986 echo "Keystone Service List"
987 keystone service-list
988 cinder quota-show \$(openstack project list | grep admin | awk {'print \$2'})
989 EOI
990   fi
991 }
992
993 ##Post configuration after install
994 ##params: none
995 function configure_post_install {
996   local opnfv_attach_networks ovs_ip ip_range net_cidr tmp_ip
997   opnfv_attach_networks="admin_network public_network"
998
999   echo -e "${blue}INFO: Post Install Configuration Running...${reset}"
1000
1001   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
1002 source overcloudrc
1003 set -o errexit
1004 echo "Configuring Neutron external network"
1005 neutron net-create external --router:external=True --tenant-id \$(keystone tenant-get service | grep id | awk '{ print \$4 }')
1006 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}
1007 EOI
1008
1009   echo -e "${blue}INFO: Checking if OVS bridges have IP addresses...${reset}"
1010   for network in ${opnfv_attach_networks}; do
1011     ovs_ip=$(find_ip ${NET_MAP[$network]})
1012     tmp_ip=''
1013     if [ -n "$ovs_ip" ]; then
1014       echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} has IP address ${ovs_ip}${reset}"
1015     else
1016       echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} missing IP, will configure${reset}"
1017       # use last IP of allocation pool
1018       eval "ip_range=\${${network}_usable_ip_range}"
1019       ovs_ip=${ip_range##*,}
1020       eval "net_cidr=\${${network}_cidr}"
1021       sudo ip addr add ${ovs_ip}/${net_cidr##*/} dev ${NET_MAP[$network]}
1022       sudo ip link set up ${NET_MAP[$network]}
1023       tmp_ip=$(find_ip ${NET_MAP[$network]})
1024       if [ -n "$tmp_ip" ]; then
1025         echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} IP set: ${tmp_ip}${reset}"
1026         continue
1027       else
1028         echo -e "${red}ERROR: Unable to set OVS Bridge ${NET_MAP[$network]} with IP: ${ovs_ip}${reset}"
1029         return 1
1030       fi
1031     fi
1032   done
1033
1034   # for virtual, we NAT public network through Undercloud
1035   if [ "$virtual" == "TRUE" ]; then
1036     if ! configure_undercloud_nat ${public_network_cidr}; then
1037       echo -e "${red}ERROR: Unable to NAT undercloud with external net: ${public_network_cidr}${reset}"
1038       exit 1
1039     else
1040       echo -e "${blue}INFO: Undercloud VM has been setup to NAT Overcloud public network${reset}"
1041     fi
1042   fi
1043
1044   # for sfc deployments we need the vxlan workaround
1045   if [ "${deploy_options_array['sfc']}" == 'true' ]; then
1046       ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
1047 source stackrc
1048 set -o errexit
1049 for node in \$(nova list | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"); do
1050 ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
1051 sudo ifconfig br-int up
1052 sudo ip route add 123.123.123.0/24 dev br-int
1053 EOF
1054 done
1055 EOI
1056   fi
1057
1058   # Collect deployment logs
1059   ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <<EOI
1060 mkdir -p ~/deploy_logs
1061 rm -rf deploy_logs/*
1062 source stackrc
1063 set -o errexit
1064 for node in \$(nova list | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"); do
1065  ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
1066  sudo cp /var/log/messages /home/heat-admin/messages.log
1067  sudo chown heat-admin /home/heat-admin/messages.log
1068 EOF
1069 scp ${SSH_OPTIONS[@]} heat-admin@\$node:/home/heat-admin/messages.log ~/deploy_logs/\$node.messages.log
1070 if [ "$debug" == "TRUE" ]; then
1071     nova list --ip \$node
1072     echo "---------------------------"
1073     echo "-----/var/log/messages-----"
1074     echo "---------------------------"
1075     cat ~/deploy_logs/\$node.messages.log
1076     echo "---------------------------"
1077     echo "----------END LOG----------"
1078     echo "---------------------------"
1079 fi
1080  ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" <<EOF
1081  sudo rm -f /home/heat-admin/messages.log
1082 EOF
1083 done
1084
1085 # Print out the dashboard URL
1086 source stackrc
1087 echo "Overcloud dashboard available at http://\$(heat output-show overcloud PublicVip | sed 's/"//g')/dashboard"
1088 EOI
1089
1090 }
1091
1092 display_usage() {
1093   echo -e "Usage:\n$0 [arguments] \n"
1094   echo -e "   -c|--config : Directory to configuration files. Optional.  Defaults to /var/opt/opnfv/ \n"
1095   echo -e "   -d|--deploy-settings : Full path to deploy settings yaml file. Optional.  Defaults to null \n"
1096   echo -e "   -i|--inventory : Full path to inventory yaml file. Required only for baremetal \n"
1097   echo -e "   -n|--net-settings : Full path to network settings file. Optional. \n"
1098   echo -e "   -p|--ping-site : site to use to verify IP connectivity. Optional. Defaults to 8.8.8.8 \n"
1099   echo -e "   -r|--resources : Directory to deployment resources. Optional.  Defaults to /var/opt/opnfv/stack \n"
1100   echo -e "   -v|--virtual : Virtualize overcloud nodes instead of using baremetal. \n"
1101   echo -e "   --no-ha : disable High Availability deployment scheme, this assumes a single controller and single compute node \n"
1102   echo -e "   --flat : disable Network Isolation and use a single flat network for the underlay network.\n"
1103   echo -e "   --no-post-config : disable Post Install configuration."
1104   echo -e "   --debug : enable debug output."
1105   echo -e "   --interactive : enable interactive deployment mode which requires user to confirm steps of deployment."
1106 }
1107
1108 ##translates the command line parameters into variables
1109 ##params: $@ the entire command line is passed
1110 ##usage: parse_cmd_line() "$@"
1111 parse_cmdline() {
1112   echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n"
1113   echo "Use -h to display help"
1114   sleep 2
1115
1116   while [ "${1:0:1}" = "-" ]
1117   do
1118     case "$1" in
1119         -h|--help)
1120                 display_usage
1121                 exit 0
1122             ;;
1123         -c|--config)
1124                 CONFIG=$2
1125                 echo "Deployment Configuration Directory Overridden to: $2"
1126                 shift 2
1127             ;;
1128         -d|--deploy-settings)
1129                 DEPLOY_SETTINGS_FILE=$2
1130                 echo "Deployment Configuration file: $2"
1131                 shift 2
1132             ;;
1133         -i|--inventory)
1134                 INVENTORY_FILE=$2
1135                 shift 2
1136             ;;
1137         -n|--net-settings)
1138                 NETSETS=$2
1139                 echo "Network Settings Configuration file: $2"
1140                 shift 2
1141             ;;
1142         -p|--ping-site)
1143                 ping_site=$2
1144                 echo "Using $2 as the ping site"
1145                 shift 2
1146             ;;
1147         -r|--resources)
1148                 RESOURCES=$2
1149                 echo "Deployment Resources Directory Overridden to: $2"
1150                 shift 2
1151             ;;
1152         -v|--virtual)
1153                 virtual="TRUE"
1154                 echo "Executing a Virtual Deployment"
1155                 shift 1
1156             ;;
1157         --no-ha )
1158                 ha_enabled="FALSE"
1159                 vm_index=1
1160                 echo "HA Deployment Disabled"
1161                 shift 1
1162             ;;
1163         --flat )
1164                 net_isolation_enabled="FALSE"
1165                 echo "Underlay Network Isolation Disabled: using flat configuration"
1166                 shift 1
1167             ;;
1168         --no-post-config )
1169                 post_config="FALSE"
1170                 echo "Post install configuration disabled"
1171                 shift 1
1172             ;;
1173         --debug )
1174                 debug="TRUE"
1175                 echo "Enable debug output"
1176                 shift 1
1177             ;;
1178         --interactive )
1179                 interactive="TRUE"
1180                 echo "Interactive mode enabled"
1181                 shift 1
1182             ;;
1183         *)
1184                 display_usage
1185                 exit 1
1186             ;;
1187     esac
1188   done
1189
1190   if [[ ! -z "$NETSETS" && "$net_isolation_enabled" == "FALSE" ]]; then
1191     echo -e "${red}INFO: Single flat network requested. Only admin_network settings will be used!${reset}"
1192   elif [[ -z "$NETSETS" ]]; then
1193     echo -e "${red}ERROR: You must provide a network_settings file with -n.${reset}"
1194     exit 1
1195   fi
1196
1197   if [[ -n "$virtual" && -n "$INVENTORY_FILE" ]]; then
1198     echo -e "${red}ERROR: You should not specify an inventory with virtual deployments${reset}"
1199     exit 1
1200   fi
1201
1202   if [[ -z "$DEPLOY_SETTINGS_FILE" || ! -f "$DEPLOY_SETTINGS_FILE" ]]; then
1203     echo -e "${red}ERROR: Deploy Settings: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}"
1204     exit 1
1205   fi
1206
1207   if [[ ! -z "$NETSETS" && ! -f "$NETSETS" ]]; then
1208     echo -e "${red}ERROR: Network Settings: ${NETSETS} does not exist! Exiting...${reset}"
1209     exit 1
1210   fi
1211
1212   if [[ ! -z "$INVENTORY_FILE" && ! -f "$INVENTORY_FILE" ]]; then
1213     echo -e "{$red}ERROR: Inventory File: ${INVENTORY_FILE} does not exist! Exiting...${reset}"
1214     exit 1
1215   fi
1216
1217   if [[ -z "$virtual" && -z "$INVENTORY_FILE" ]]; then
1218     echo -e "${red}ERROR: You must specify an inventory file for baremetal deployments! Exiting...${reset}"
1219     exit 1
1220   fi
1221
1222   if [[ "$net_isolation_enabled" == "FALSE" && "$post_config" == "TRUE" ]]; then
1223     echo -e "${blue}INFO: Post Install Configuration will be skipped.  It is not supported with --flat${reset}"
1224     post_config="FALSE"
1225   fi
1226
1227   ##LIBRARIES
1228   # Do this after cli parse so that $CONFIG is set properly
1229   source $CONFIG/lib/common-functions.sh
1230   source $CONFIG/lib/installer/onos/onos_gw_mac_update.sh
1231
1232 }
1233
1234 ##END FUNCTIONS
1235
1236 main() {
1237   parse_cmdline "$@"
1238   echo -e "${blue}INFO: Parsing network settings file...${reset}"
1239   parse_network_settings
1240   if ! configure_deps; then
1241     echo -e "${red}Dependency Validation Failed, Exiting.${reset}"
1242     exit 1
1243   fi
1244   if [ -n "$DEPLOY_SETTINGS_FILE" ]; then
1245     parse_deploy_settings
1246   fi
1247   setup_undercloud_vm
1248   if [ "$virtual" == "TRUE" ]; then
1249     setup_virtual_baremetal
1250   elif [ -n "$INVENTORY_FILE" ]; then
1251     parse_inventory_file
1252   fi
1253   configure_undercloud
1254   undercloud_prep_overcloud_deploy
1255   if [ "$post_config" == "TRUE" ]; then
1256     if ! configure_post_install; then
1257       echo -e "${red}ERROR:Post Install Configuration Failed, Exiting.${reset}"
1258       exit 1
1259     else
1260       echo -e "${blue}INFO: Post Install Configuration Complete${reset}"
1261     fi
1262   fi
1263   if [[ "${deploy_options_array['sdn_controller']}" == 'onos' ]]; then
1264     if ! onos_update_gw_mac ${public_network_cidr} ${public_network_gateway}; then
1265       echo -e "${red}ERROR:ONOS Post Install Configuration Failed, Exiting.${reset}"
1266       exit 1
1267     else
1268       echo -e "${blue}INFO: ONOS Post Install Configuration Complete${reset}"
1269     fi
1270   fi
1271 }
1272
1273 main "$@"