290 lines
11 KiB
Bash
Executable File
290 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Copyright 2013 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
# This file contains the functions for connecting to Fuel VM, checking if the installation process completed
|
|
# and Fuel became operational, and also enabling outbound network/internet access for this VM through the
|
|
# host system
|
|
|
|
[ "$(basename ${0})" = "product.sh" ] && exit 1
|
|
|
|
source ./functions/shell.sh
|
|
|
|
ssh_options='-oConnectTimeout=5 -oStrictHostKeyChecking=no -oCheckHostIP=no -oUserKnownHostsFile=/dev/null -oRSAAuthentication=no -oPubkeyAuthentication=no'
|
|
|
|
wait_for_exec_in_bootstrap() {
|
|
ip=$1
|
|
username=$2
|
|
password=$3
|
|
prompt=$4
|
|
cmd=$5
|
|
|
|
# Log in into the VM, exec cmd and print exitcode
|
|
# Looks a bit ugly, but 'end of expect' has to be in the very beginning of the line
|
|
result=$(
|
|
execute expect << ENDOFEXPECT
|
|
spawn ssh $ssh_options $username@$ip
|
|
expect "connect to host" exit
|
|
expect "*?assword:*"
|
|
send "$password\r"
|
|
expect "$prompt"
|
|
send "$cmd\r"
|
|
expect "$prompt"
|
|
send "echo \"rc=\$?\"\r"
|
|
expect "$prompt"
|
|
send "logout\r"
|
|
expect "$prompt"
|
|
ENDOFEXPECT
|
|
)
|
|
echo "$result" | grep -q "[r]c=0" >&2 && return 0
|
|
return 1
|
|
}
|
|
|
|
wait_for_product_vm_to_download() {
|
|
ip=$1
|
|
username=$2
|
|
password=$3
|
|
prompt=$4
|
|
|
|
echo -n "Waiting for product VM to download files. Please do NOT abort the script... "
|
|
|
|
# Loop until master node booted and wait_for_external_config started
|
|
maxdelay=3000
|
|
while ! wait_for_exec_in_bootstrap $ip $username $password "$prompt" "ps xa | grep '\[w\]ait_for_external_config'"; do
|
|
sleep 5
|
|
((waited += 5))
|
|
if (( waited >= maxdelay )); then
|
|
echo "Installation timed out! ($maxdelay seconds)" 1>&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "OK"
|
|
}
|
|
|
|
wait_for_product_vm_to_install() {
|
|
ip=$1
|
|
username=$2
|
|
password=$3
|
|
prompt=$4
|
|
|
|
echo -n "Waiting for product VM to install. Please do NOT abort the script... "
|
|
|
|
# Loop until master node gets successfully installed
|
|
maxdelay=3000
|
|
while wait_for_exec_in_bootstrap $ip $username $password "$prompt" "ps xa | grep '\[b\]ootstrap_admin_node.sh'"; do
|
|
sleep 5
|
|
((waited += 5))
|
|
if (( waited >= maxdelay )); then
|
|
echo "Installation timed out! ($maxdelay seconds)" 1>&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "OK"
|
|
}
|
|
|
|
check_internet_connection() {
|
|
line=$1
|
|
OIFS="${IFS}"
|
|
NIFS=$' '
|
|
IFS="${NIFS}"
|
|
for i in $line; do
|
|
if [[ "$i" == *% && "$i" != 100* ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
IFS="${OIFS}"
|
|
return 1
|
|
}
|
|
|
|
enable_outbound_network_for_product_vm() {
|
|
ip=$1
|
|
username=$2
|
|
password=$3
|
|
prompt=$4
|
|
|
|
# Check for internet access on the host system
|
|
echo -n "Checking for internet connectivity on the host system... "
|
|
check_hosts=`echo google.com wikipedia.com | tr ' ' '\n'`
|
|
case $(execute uname) in
|
|
Linux | Darwin)
|
|
for i in ${check_hosts} ; do
|
|
ping_host=`execute ping -c 2 ${i} | grep %`
|
|
ping_host_result+=$ping_host
|
|
done
|
|
;;
|
|
CYGWIN*)
|
|
if [ ! -z "`execute type ping | grep system32`" ]; then
|
|
for i in ${check_hosts} ; do
|
|
ping_host=`execute ping -n 5 ${i} | grep %`
|
|
ping_host_result+=$ping_host
|
|
done
|
|
elif [ ! -z "`execute type ping | grep bin`" ]; then
|
|
for i in ${check_hosts} ; do
|
|
ping_host=`execute ping ${i} count 5 | grep %`
|
|
ping_host_result+=$ping_host
|
|
done
|
|
else
|
|
print_no_internet_connectivity_banner
|
|
fi
|
|
;;
|
|
*)
|
|
print_no_internet_connectivity_banner
|
|
;;
|
|
esac
|
|
|
|
check_internet_connection "$ping_host_result"
|
|
if [[ $? -eq 0 ]]; then
|
|
echo "OK"
|
|
else
|
|
print_no_internet_connectivity_banner
|
|
fi
|
|
|
|
# Check host nameserver configuration
|
|
echo -n "Checking local DNS configuration... "
|
|
if execute test -f /etc/resolv.conf ; then
|
|
# we should exclude loopback and IPv6 addresses from the nameservers list
|
|
nameserver="$(execute grep '^nameserver' /etc/resolv.conf | egrep -v 'nameserver\s*(127\.|.*:)' | head -3)"
|
|
fi
|
|
if [ -z "$nameserver" ] && execute test -x /usr/bin/nmcli; then
|
|
# Get DNS from network manager
|
|
if [ -n "`execute LANG=C nmcli nm | grep \"running\s\+connected\"`" ]; then
|
|
# we should exclude loopback and IPv6 addresses from the nameservers list
|
|
nameserver="$(execute nmcli dev list | grep 'IP[46].DNS' | sed -e 's/IP[46]\.DNS\[[0-9]\+\]:\s\+/nameserver /'| grep -v 'nameserver\s*(127\.|.*:)' | head -3)"
|
|
fi
|
|
fi
|
|
if [ -z "$nameserver" ]; then
|
|
echo "/etc/resolv.conf does not contain a nameserver. Using 8.8.8.8 for DNS."
|
|
nameserver="nameserver 8.8.8.8"
|
|
else
|
|
echo "OK"
|
|
fi
|
|
|
|
# Enable internet access on inside the VMs
|
|
echo -n "Enabling outbound network/internet access for the product VM... "
|
|
|
|
# Get network settings (ip address and ip network) for eth1 interface of the master node
|
|
local master_ip_pub_net=$(echo $fuel_master_ips | cut -f2 -d ' ')
|
|
master_ip_pub_net="${master_ip_pub_net%.*}"".1"
|
|
local master_pub_net="${master_ip_pub_net%.*}"".0"
|
|
|
|
# Convert nameservers list into the one line separated by the comma
|
|
dns_upstream="$(echo -e $nameserver | cut -d ' ' -f2 | sed -e':a;N;$!ba;s/\n/,/g')"
|
|
|
|
# Log in into the VM, configure and bring up the NAT interface, set default gateway, check internet connectivity
|
|
# Looks a bit ugly, but 'end of expect' has to be in the very beginning of the line
|
|
result=$(
|
|
execute expect << ENDOFEXPECT
|
|
spawn ssh $ssh_options $username@$ip
|
|
expect "connect to host" exit
|
|
expect "*?assword:*"
|
|
send "$password\r"
|
|
expect "$prompt"
|
|
# make backups, remove network manager options, disable defaults, enable boot and disable network manager
|
|
send "sed -i.orig '/^UUID=\\\|^NM_CONTROLLED=/d;s/^\\\(.*\\\)=yes/\\\1=no/g;s/^ONBOOT=.*/ONBOOT=yes/;/^ONBOOT=/iNM_CONTROLLED=no' /etc/sysconfig/network-scripts/ifcfg-eth{0,1,2}\r"
|
|
expect "$prompt"
|
|
# eth1 should be static with private ip address and provided netmask
|
|
send "sed -i 's/^BOOTPROTO=.*/BOOTPROTO=static/;/^BOOTPROTO/aIPADDR=${master_ip_pub_net}\\\nNETMASK=${mask}' /etc/sysconfig/network-scripts/ifcfg-eth1\r"
|
|
expect "$prompt"
|
|
# eth2 should get ip address via dhcp and used default route
|
|
send "sed -i 's/^BOOTPROTO=.*/BOOTPROTO=dhcp/;s/^DEFROUTE=.*/DEFROUTE=yes/;/^BOOTPROTO/aPERSISTENT_DHCLIENT=yes' /etc/sysconfig/network-scripts/ifcfg-eth2\r"
|
|
expect "$prompt"
|
|
# make backup and disable zeroconf at all because we should use only DHCP on eth2
|
|
send "sed -i.orig '/NOZEROCONF/d;aNOZEROCONF=yes' /etc/sysconfig/network\r"
|
|
expect "$prompt"
|
|
# remove default route from eth0 and system wide settings if exists
|
|
send "sed -i '/^GATEWAY=/d' /etc/sysconfig/network /etc/sysconfig/network-scripts/ifcfg-eth0\r"
|
|
expect "$prompt"
|
|
# fix bug https://bugs.centos.org/view.php?id=7351
|
|
send "sed -i.orig '/^DEVICE=lo/aTYPE=Loopback' /etc/sysconfig/network-scripts/ifcfg-lo\r"
|
|
expect "$prompt"
|
|
# remove old settings from the resolv.conf and dnsmasq.upstream if exists
|
|
send "sed -i.orig '/^nameserver/d' /etc/resolv.conf /etc/dnsmasq.upstream &>/dev/null\r"
|
|
expect "$prompt"
|
|
# update the resolv.conf and dnsmasq.upstream with the new settings
|
|
send "echo -e '$nameserver' | tee -a /etc/dnsmasq.upstream >>/etc/resolv.conf\r"
|
|
expect "$prompt"
|
|
# update the astute.yaml with the new settings
|
|
send "sed -i.orig '/DNS_UPSTREAM/c\\"DNS_UPSTREAM\\": \\"${dns_upstream}\\"' /etc/fuel/astute.yaml\r"
|
|
expect "$prompt"
|
|
# enable NAT (MASQUERADE) and forwarding for the public network
|
|
send "/sbin/iptables -t nat -A POSTROUTING -s $master_pub_net/24 \! -d $master_pub_net/24 -j MASQUERADE\r"
|
|
expect "$prompt"
|
|
send "/sbin/iptables -I FORWARD 1 --dst $master_pub_net/24 -j ACCEPT\r"
|
|
expect "$prompt"
|
|
send "/sbin/iptables -I FORWARD 1 --src $master_pub_net/24 -j ACCEPT\r"
|
|
expect "$prompt"
|
|
send "service iptables save &>/dev/null\r"
|
|
expect "$prompt"
|
|
# disable NetworkManager and apply the network changes
|
|
send "nmcli networking off &>/dev/null ; service network restart &>/dev/null\r"
|
|
expect "$prompt"
|
|
send "logout\r"
|
|
expect "$prompt"
|
|
ENDOFEXPECT
|
|
)
|
|
echo "OK"
|
|
|
|
# Waiting until the network services are restarted.
|
|
# 5 seconds is optimal time for different operating systems.
|
|
echo -n "Waiting until the network services are restarted... "
|
|
sleep 5s
|
|
result_inet=$(
|
|
execute expect << ENDOFEXPECT
|
|
spawn ssh $ssh_options $username@$ip
|
|
expect "connect to host" exit
|
|
expect "*?assword:*"
|
|
send "$password\r"
|
|
expect "$prompt"
|
|
send "for i in {1..5}; do ping -c 2 google.com || ping -c 2 wikipedia.com || sleep 2; done\r"
|
|
expect "*icmp*"
|
|
expect "$prompt"
|
|
send "logout\r"
|
|
expect "$prompt"
|
|
ENDOFEXPECT
|
|
)
|
|
|
|
# When you are launching command in a sub-shell, there are issues with IFS (internal field separator)
|
|
# and parsing output as a set of strings. So, we are saving original IFS, replacing it, iterating over lines,
|
|
# and changing it back to normal
|
|
#
|
|
# http://blog.edwards-research.com/2010/01/quick-bash-trick-looping-through-output-lines/
|
|
OIFS="${IFS}"
|
|
NIFS=$'\n'
|
|
IFS="${NIFS}"
|
|
|
|
for line in $result_inet; do
|
|
IFS="${OIFS}"
|
|
if [[ $line == *icmp_seq* ]]; then
|
|
IFS="${NIFS}"
|
|
echo "OK"
|
|
wait_for_exec_in_bootstrap $ip $username $password "$prompt" "pkill -f ^wait_for_external_config"
|
|
return 0;
|
|
fi
|
|
IFS="${NIFS}"
|
|
done
|
|
print_no_internet_connectivity_banner
|
|
return 1
|
|
}
|
|
|
|
print_no_internet_connectivity_banner() {
|
|
echo "FAIL"
|
|
echo "############################################################"
|
|
echo "# WARNING: some of the Fuel features will not be supported #"
|
|
echo "# because there is no Internet connectivity #"
|
|
echo "############################################################"
|
|
}
|