mars/test_suite/scripts/lib/lib.sh

305 lines
9.3 KiB
Bash

#!/usr/bin/env bash
# Copyright 2010-2014 Frank Liepold / 1&1 Internet AG
# Email: frank.liepold@1und1.de
#
# Key note: Thomas Schoebel-Theuer
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# this may be later overridden by distros / install scripts / etc
# $sript_dir is assumed to be already set by the caller
base_dir="$(cd "$script_dir/.."; pwd)"
#####################################################################
# general error exit function
function lib_callstack
{
echo "================= Callstack ===================================================="
local argv_index=0 i
for i in ${!FUNCNAME[*]}; do
local j args=
argc=${BASH_ARGC[$i]}
if [ ${argc:-0} -gt 0 ]; then
for j in $(seq 1 1 $argc); do
args='"'"${BASH_ARGV[$argv_index]}"'" '"$args"
let argv_index+=1
done
fi
echo ${BASH_SOURCE[(($i + 1))]:-"stdin"}:${BASH_LINENO[$i]} ${FUNCNAME[$i]} $args
done
echo "================= End callstack ================================================"
}
function lib_exit
{
local rc=$1 msg="$2"
if [ -n "$msg" ];then
echo " $msg" >&2
fi
if [ $rc -ne 0 -a -z "$lib_exit_recursion" ]; then
lib_callstack >&2
fi
if [ $rc -ne $main_prevent_remove_lock_files_exit_code ]; then
release_host_locks
fi
# to avoid recursion
if [ -n "$lib_exit_recursion" ];then
echo "lib_exit:recursion!!!" >&2
printf "\nstack:\n" >&2
lib_callstack >&2
exit $rc
fi
export lib_exit_recursion=1
eval $lib_general_checks_after_every_test_function
if [ ${#main_error_recovery_functions[*]} -ge 0 ]; then
local func args
for func in "${!main_error_recovery_functions[@]}"; do
args="${main_error_recovery_functions[$func]}"
if [ -n "$args" ];then
echo " calling error recovery $func" >&2
$func $args || exit 1
fi
done
fi
exit $rc
}
#####################################################################
# helper to generate verbose messages
function lib_vmsg
{
if (( main_verbose_script )); then
echo "$(date +'%Y-%m-%d %H:%M:%S') [[$(
for i in $(seq $((${#BASH_SOURCE[@]} - $main_min_stack_level)) -1 1); do
printf '%s' $prefix$(basename ${BASH_SOURCE[$i]}):${BASH_LINENO[$(($i - 1))]}
prefix='->'
done
)]]" $*
fi
}
#####################################################################
# helper for prevention of script failures due to missing tools
function check_installed
{
local check_list="$1" i
for i in $check_list; do
if ! which $i >/dev/null 2>&1; then
echo "Sorry, program '$i' is not installed."
exit -1
fi
done
}
check_always_list="basename dirname which pwd mkdir rmdir rm cat ls sort ssh scp nice sed awk"
check_installed "$check_always_list"
#####################################################################
# helper for sourcing other config files (may reside in parents of cwd)
function source_config
{
local name="$1"
local setup_dir=$(pwd)
local pwd=$(pwd)
local limit=0
local setup
until [ -r $setup_dir/$name.conf ]; do
setup_dir="$(cd $setup_dir/..; pwd)"
(( limit++ > 20 )) && { echo "config file $name.conf not found in parent directories of $pwd."; return 1; }
done
setup=$setup_dir/$name.conf
echo "Sourcing config file $setup"
shopt -u nullglob
source $setup || exit $?
return 0
}
#####################################################################
# abstracting access to remote hosts
function lib_remote_opt
{
local ssh_opt="$1"
shift
local host="$1"
shift
ssh $ssh_opt -n root@"$host" "$@"
}
function remote
{
lib_remote_opt "" "$@"
}
function lib_remote_all_idfile
{
lib_remote_all_opt "$main_ssh_idfile_opt" "$@"
}
function lib_remote_all_opt
{
local ssh_opt="$1"
shift
local host_all="$1" host
shift
local cmd="$@"
for host in $host_all; do
lib_remote_opt "$ssh_opt" "$host" "$cmd" \
|| { rc=$?; echo " $cmd cmd failed on host $host" >&2; return $rc; }
done
}
function remote_all
{
lib_remote_all_opt "" "$@"
}
function lib_remote_idfile
{
lib_remote_opt "$main_ssh_idfile_opt" "$@"
}
function remote_all_noreturn
{
local host_all="$1" host
shift
for host in $host_all; do
remote "$host" "$@"
done
}
function lib_check_access_to_remote_hosts
{
local ssh_opt="$1"
shift
local hostlist="$@"
if (( main_verbose_script )); then
echo " testing access as root to hosts $hostlist"
fi
lib_remote_all_opt "$ssh_opt" "$hostlist" hostname || lib_exit 1
}
# The pid of the started program will be returned in the variable named by
# $3.
# The names of the script which is executed on the remote host will be returned
# in the variable namend by $4
# The name of the (remote) outputfile of the script is the script name
# extended with .out
# stdout resp. stderr are kept in files named <script>.out resp. <script>.err on the remote host.
function lib_start_script_remote_bg
{
[ $# -eq 5 ] || lib_exit 1 "wrong number $# of arguments (args = $*)"
local host=$1 script=$2 varname_pid=$3 varname_script=$4 rm_opt=$5
local ssh_opt="$main_ssh_idfile_opt"
if [ ! -f $script ]; then
lib_exit 1 "script file $script not found"
fi
chmod ugo+x $script || lib_exit 1 "cannot chmod $script"
local remote_filename=/tmp/$(basename $script)
lib_vmsg " copying script $script to $host:$remote_filename"
scp $ssh_opt $script root@$host:$remote_filename || lib_exit 1
if [ $rm_opt = "rm" ]; then
rm -f $script || lib_exit 1
fi
local remote_pid
local cmd="/bin/bash $remote_filename >$remote_filename.out 2>$remote_filename.err"
local error_hint="see also $remote_filename.out resp. $remote_filename.err"
lib_vmsg " executing $cmd on $host"
remote_pid=$(ssh -n $ssh_opt root@$host ''"$cmd"' & echo $!') || lib_exit 1 "$error_hint"
lib_vmsg " checking whether process $remote_pid is running on $host"
ssh -n $ssh_opt root@$host ps -fp $remote_pid || lib_exit 1 "$error_hint"
lib_vmsg " checking whether process $remote_pid has errors in $remote_filename.err"
ssh -n $ssh_opt root@$host "if [ -s $remote_filename.err ]; then cat $remote_filename.err; echo killing $remote_pid; kill -1 $remote_pid; exit 1; fi" || lib_exit 1
eval $varname_pid=$remote_pid
eval $varname_script=$remote_filename
}
## copy a file from a remote host to a local target
function lib_cp_remote_file
{
local host=$1 remote_filename=$2 local_filename=$3
local ssh_opt="$main_ssh_idfile_opt"
lib_vmsg " cp $host:$remote_filename -> $local_filename"
scp $ssh_opt root@$host:$remote_filename $local_filename || lib_exit 1
}
#####################################################################
# generate copyright header on stdout
function echo_copyright
{
local name="$1"
local copyright="${2:-Thomas Schoebel-Theuer / 1&1 Internet AG}"
# Notice: the following GNU all-permissive license applies to the
# generated DATA file only, and does not change the GPL of this script.
#
echo "Copyright $copyright"
echo ""
if [ -n "$name" ]; then
echo "This file was automatically generated from '$name'"
echo "converted by $(whoami)@$(hostname) $(date)"
echo ""
fi
echo "PLEASE DO NOT EDIT this file without renaming, even if legally"
echo "allowed by the following GNU all-permissive license:"
echo ""
echo "Copying and distribution of this file, with or without modification,"
echo "are permitted in any medium without royalty provided the copyright"
echo "notice and this notice are preserved. This file is offered as-is,"
echo "without any warranty."
echo ""
echo "PLEASE name any derivatives of this file DIFFERENTLY, in order to"
echo "avoid confusion. Additionally, PLEASE add a pointer to the original."
echo ""
echo "PLEASE means: failing to do so may damage your reputation."
echo ""
echo "Why? Because people EXPECT that 'things' remain the same, otherwise"
echo "they may accuse you of winding them up."
echo ""
echo "Notice: damaged reputation can be harder than prison. I have warned you."
echo ""
echo "In practice: although I don't put a 'hard' requirement on you,"
echo "PLEASE just copy/rename this file before doing"
echo "any modifications, and include a pointer to the original."
echo ""
echo "Additionally, it is best practice to name your data files such that"
echo "other people can easily grasp what is inside."
echo ""
echo "#################################################################"
}