#/bin/bash
# Debug=1 - Verbose Logging
# Debug= - Limited logging
DEBUG=
# Logging
TIME=`date +%F_%H.%M.%S`
LOGDIR="./logs"
LOGFILE="$LOGDIR/$TIME.log"
ACTIVE_NODES="$LOGDIR/active_nodes"
# SSH
SSH_KEY_PATH="id_rsa"
SLICE_NAME="usf_ubcslice3"
# Remote Directory
REMOTE_HOME="/home/usf_ubcslice3"
REMOTE_BUILD_DIR="$REMOTE_HOME/build"
# pyFacebook
SVN_REPO_PYFACEBOOK_PATH="pyfacebook"
SVN_REPO_PYFACEBOOK_URL="http://pyfacebook.googlecode.com/svn/trunk/"
# Git
GIT_REPO_UTIL_PATH="$REMOTE_HOME/util"
GIT_REPO_UTIL_URL="git://github.com/MTsoul/util.git"
GIT_REPO_JOKE_PATH="$REMOTE_HOME/joke"
GIT_REPO_JOKE_URL="git://oliverzheng.com/ubc/eece411/joke.git"
# Python
PYTHON_VERSION="2.6"
PYTHON_PKG_NAME="Python-2.6.4"
PYTHON_PKG_TYPE="tgz"
PYTHON_URL="http://www.python.org/ftp/python/2.6.4/$PYTHON_PKG_NAME.$PYTHON_PKG_TYPE"
PYTHON_INSTALL_PREFIX="/opt/python$PYTHON_VERSION"
# Python Setup Tools
SETUP_TOOLS_PKG_NAME="setuptools-0.6c11"
SETUP_TOOLS_PKG_TYPE="tar.gz"
SETUP_TOOLS_URL="http://pypi.python.org/packages/source/s/setuptools/$SETUP_TOOLS_PKG_NAME.$SETUP_TOOLS_PKG_TYPE#md5=7df2a529a074f613b509fb44feefe74e"
# Python Memcache
MEMCACHE_PKG_NAME="python-memcached-1.45"
MEMCACHE_PKG_TYPE="tar.gz"
MEMCACHE_URL="ftp://ftp.tummy.com/pub/python-memcached/$MEMCACHE_PKG_NAME.$MEMCACHE_PKG_TYPE"
# Python Twisted
TWISTED_PKG_NAME="Twisted-8.2.0"
TWISTED_PKG_TYPE="tar.bz2"
TWISTED_URL="http://tmrc.mit.edu/mirror/twisted/Twisted/8.2/$TWISTED_PKG_NAME.$TWISTED_PKG_TYPE"
# Start Server
SERVER_LOG_FILE="$GIT_REPO_UTIL_PATH/log-start_server"
SERVER_EXECUTABLE="start_server.py"
#
# $1 = node hostname/IP
# $2 = command
#
function remote_exec() {
# ssh without host key checking
cmd="ssh -i $SSH_KEY_PATH -l $SLICE_NAME -oStrictHostKeyChecking=no $1"
if [ ! -z "$TIMEOUT" ]
then
cmd="$cmd -oConnectTimeout=$TIMEOUT"
fi
if [ "$4" == "nohup" ]
then
cmd="$cmd \"nohup $2 &\""
else
cmd="$cmd \"$2\""
fi
if [ ! -z "$DEBUG" ]
then
log $cmd
fi
ret=`eval $cmd 2>&1`
ret_val=$?
if [ $ret_val -ne 0 ]
then
log "Remote Execution Error: $1 with message $ret"
# connection problem, send signal to trap
if [ $ret_val -eq 255 ]
then
kill -5 $$
else
return 1
fi
fi
echo $ret
}
function log() {
echo `date +%F_%H.%M.%S` - $* >> $LOGFILE
}
function log_and_print() {
echo $*
log $*
}
function init() {
dir_name=`dirname $LOGFILE`
mkdir -pv "$dir_name"
touch $ACTIVE_NODES
}
function get_python_version() {
ret_val=`remote_exec $1 "python -V"`
if [ $? -ne 0 ]
then
echo "0"
fi
echo `echo $ret_val | sed 's/^.*Python //g'`
}
function get_os_version() {
os_ver=`remote_exec $1 "cat /etc/fedora-release"`
if [ $? -eq 0 ] && ! [[ "$os_ver" =~ "Fedora release 8".* ]]
then
log "WARNING: $1 OS is NOT Fedora 8 but $os_ver"
fi
log "$os_ver"
}
#
# $1 Hostname
# $2 Git Remote Repo
# $3 Git Remote Directory
#
function git_clone() {
not_exists=`remote_exec $1 "if ! [ -e '$3' ]; then echo 1; fi"`
if [ $? -eq 0 ] && [ $not_exists ]
then
log "git: cloning $2"
ret=`remote_exec $1 "cd $REMOTE_HOME; git clone $2"`
else
log "git: $3 has already been cloned"
log "git: removing old $3"
ret=`remote_exec $1 "rm -rf $3"`
log "git: cloning $2"
ret=`remote_exec $1 "cd ~; git clone $2"`
fi
}
#
# $1 Hostname
# $2 Git Remote Repo
#
function git_pull() {
log "git: pull $2"
ret=`remote_exec $1 "cd $2; git pull"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to perform git pull $2"
fi
}
function git_submodule() {
log "git: submodule $2"
ret=`remote_exec $1 "cd $2; git submodule init; git submodule update;"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to update submodule $2"
fi
}
function install() {
case $2 in
python)
upgrade_python $1
;;
python-setuptools)
install_python_package $1 $SETUP_TOOLS_PKG_NAME $SETUP_TOOLS_PKG_TYPE $SETUP_TOOLS_URL
;;
python-memcache)
install_python_package $1 $MEMCACHE_PKG_NAME $MEMCACHE_PKG_TYPE $MEMCACHE_URL
;;
python-twisted)
install_python_package $1 $TWISTED_PKG_NAME $TWISTED_PKG_TYPE $TWISTED_URL
;;
pyfacebook)
install_pyfacebook $1
;;
*)
# Yum requires python2.5 to be default
ver=`get_python_version $1`
if [ ${ver:0:1} != "2" ] || [ ${ver:2:1} != "5" ]
then
log "$2: symlink /usr/bin/python2.5 -> /usr/bin/python"
ret=`remote_exec $1 "sudo ln -sf /usr/bin/python2.5 /usr/bin/python"`
fi
ret=`remote_exec $1 "sudo yum install -y $2"`
if [[ $ret == *Nothing\ to\ do* ]]
then
log "$2: Already installed"
else
log "$2: Installing"
fi
;;
esac
}
function upgrade_python() {
ver=`get_python_version $1`
# Default python version < $PYTHON_VERSION
if [ "${ver:0:1}" -lt ${PYTHON_VERSION:0:1} ] || \
{ [ "${ver:0:1}" -eq ${PYTHON_VERSION:0:1} ] && [ "${ver:2:1}" -lt ${PYTHON_VERSION:2:1} ];}
then
# Determine if $PYTHON_VERSION has already been installed
not_exists=`remote_exec $1 "if ! [ -e '$PYTHON_INSTALL_PREFIX/bin/python' ]; then echo 1; fi"`
if [ $? -eq 0 ] && [ $not_exists ]
then
if [ "${ver:0:1}" -eq 0 ]
then
log "python: Installing python$PYTHON_VERSION"
else
log "python: Upgrading from $ver to $PYTHON_VERSION"
fi
# Download
clean_file $1 $PYTHON_PKG_NAME.$PYTHON_PKG_TYPE
log "python: Downloading package from $PYTHON_URL"
download $1 $PYTHON_URL
# Unpack
log "python: Unpacking..."
extract $1 $PYTHON_PKG_NAME.$PYTHON_PKG_TYPE
# Install
log "python: Installing..."
ret=`remote_exec $1 "cd $REMOTE_BUILD_DIR/$PYTHON_PKG_NAME; \
./configure --prefix=$PYTHON_INSTALL_PREFIX --with-zlib=/usr/include; \
make; \
sudo make install;"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to install python"
fi
# Clean
log "python: Clean..."
clean_file $1 $PYTHON_PKG_NAME.$PYTHON_PKG_TYPE
fi
# Replace default 'python'
log "python: symlink $PYTHON_INSTALL_PREFIX/bin/python -> /usr/bin/python"
ret=`remote_exec $1 "sudo ln -sf $PYTHON_INSTALL_PREFIX/bin/python /usr/bin/python"`
else
log "python: v$PYTHON_VERSION already installed"
fi
}
#
# $1 hostname
# $2 package name
# $3 package type
# $4 package download location (url)
#
function install_python_package() {
# Make sure $PYTHON_VERSION is default
upgrade_python $1
# Download
log "$2: Downloading from $4..."
clean_file $1 $2.$3
download $1 $4
# Unpack
log "$2: Unpacking..."
extract $1 $2.$3
# Install
log "$2: Building/Installing..."
ret=`remote_exec $1 "cd $REMOTE_BUILD_DIR/$2; \
sudo python setup.py install;"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to install $2"
fi
# Clean
log "$2: Clean..."
clean_file $1 $2.$3
}
function download() {
ret=`remote_exec $1 "mkdir -p $REMOTE_BUILD_DIR; \
cd $REMOTE_BUILD_DIR; \
wget $2;"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to download from $2"
fi
}
function extract() {
case $2 in
*.tar.bz2) cmd="tar xvjf $2" ;;
*.tar.gz) cmd="tar xvzf $2" ;;
*.tar) cmd="tar xvf $2" ;;
*.tgz) cmd="tar xvzf $2" ;;
esac
if [ -z "${cmd+xxx}" ]
then
log_and_print "ERROR: Don't know how to extract $2"
exit
fi
ret=`remote_exec $1 "cd $REMOTE_BUILD_DIR; \
${cmd}"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to extract $2"
exit
fi
}
function clean_file() {
ret=`remote_exec $1 "cd $REMOTE_BUILD_DIR; \
rm -f $2;"`
}
function install_pyfacebook() {
upgrade_python $1
log "pyfacebook: Installing"
ret=`remote_exec $1 "svn checkout $SVN_REPO_PYFACEBOOK_URL $REMOTE_BUILD_DIR/$SVN_REPO_PYFACEBOOK_PATH;"`
ret=`remote_exec $1 "cd $REMOTE_BUILD_DIR/$SVN_REPO_PYFACEBOOK_PATH; \
sudo python setup.py install"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to install pyfacebook"
fi
}
function start_server() {
ret=`remote_exec $1 "killall $SERVER_EXECUTABLE"`
remote_host=`remote_exec $1 "curl -s http://checkip.dyndns.org/ | grep -o '[[:digit:].]\+'"`
log "server: starting server on $remote_host"
ret=`remote_exec $1 "$GIT_REPO_UTIL_PATH/$SERVER_EXECUTABLE $remote_host &> $SERVER_LOG_FILE &"`
if [ $? -ne 0 ]
then
log_and_print "ERROR: unable to start server"
exit
fi
}
function load_node_list() {
#check if file exists here
n=0
while read line
do
n=$(($n+1));
# skip commented node
if [[ ${line:0:1} == "#" ]]
then
continue;
fi
list[n]=$line;
done < "$1"
}
#
# $1 = timeout
#
if [ $# -eq 0 ]
then
echo "Usage: ./bootstrap.sh < [node_list_filepath] [timeout]"
echo " or"
echo " echo -e \"[hostname1]\n[hostname2]\" | ./bootstrap.sh [timeout]"
exit 1
fi
if ! [ $1 -eq $1 2>/dev/null ]
then
echo "Timeout must be an Integer"
exit 1
fi
F_NODELIST="/dev/stdin"
TIMEOUT=$1
load_node_list $F_NODELIST
init
log_and_print "Starting bootstrap"
log "Connection Timeout is set to: ${TIMEOUT}s"
trap "continue;" 5
start=`date +"%s"`
# Iterate through nodes
i=0
for node in ${list[@]}
do
i=$(($i+1))
now=`date +"%s"`
hostname=`echo $node | sed s/,.*$//g`
ip=`echo $node | sed s/^.*,//g`
log_and_print "=== (${i} of ${#list[@]}) $hostname ==="
# OS
get_os_version $hostname
# Python2.6 breaks yum. As a result, the installation of
# the packages below will fail if yum python2.6 is the
# default python
install $hostname "make"
install $hostname "gcc"
install $hostname "git"
install $hostname "svn"
install $hostname "zlib"
install $hostname "zlib-devel"
install $hostname "python"
install $hostname "python-setuptools"
install $hostname "python-memcache"
install $hostname "python-twisted"
install $hostname "pyfacebook"
git_clone $hostname $GIT_REPO_JOKE_URL $GIT_REPO_JOKE_PATH
git_pull $hostname $GIT_REPO_JOKE_PATH
git_clone $hostname $GIT_REPO_UTIL_URL $GIT_REPO_UTIL_PATH
git_pull $hostname $GIT_REPO_UTIL_PATH
git_submodule $hostname $GIT_REPO_UTIL_PATH
start_server $hostname
then=`date +"%s"`
log "Finished $hostname in $(($then - $now))s"
# get this far, save this node as usable
if [ `grep -c $hostname $ACTIVE_NODES` -eq 0 ]
then
echo $hostname >> $ACTIVE_NODES
fi
done
end=`date +"%s"`
log_and_print "Finished bootstrapping in $(($end - $start))s"