training-guides/labs/lib/osbash/functions.host
Roger Luethi 3f1ef07fdd labs: osbashauto exit on script error
Make osbashout abort if a client-side scripts returns an error; use a
file named error in the status directory to signal the problem and to
store the name of the offending script for use by Windows batch scripts.

Have osbash.sh and the Windows batch scripts exit when an error file
appears.

Change-Id: I6722e75c9c23b99e5ed25b00429026c2e0ff6a12
2014-09-14 10:55:40 +02:00

390 lines
11 KiB
Bash

# This file contains bash functions that are used by osbash on the host.
source "$LIB_DIR/functions"
#-------------------------------------------------------------------------------
# Conditional execution
#-------------------------------------------------------------------------------
# TODO: Create a help function and display it under help by default or with
# option --help (-h).
# exec_cmd is used for conditional execution:
#
# OSBASH=exec_cmd
#
# Execute command only if OSBASH is set:
# ${OSBASH:-:} cmd args
#
# Execute command only if OSBASH is not set:
# ${OSBASH:+:} cmd args
#
# Disable actual call to VBoxManage (selectively override configuration):
# OSBASH= cmd args
#
# Enable call to VBoxManage (selectively override configuration):
# OSBASH=exec_cmd cmd args
function exec_cmd {
local cmd=$1
shift
$cmd "$@"
}
#-------------------------------------------------------------------------------
function get_base_disk_path {
echo $DISK_DIR/base-$VM_ACCESS-$DISTRO.vdi
}
#-------------------------------------------------------------------------------
# ssh
#-------------------------------------------------------------------------------
# Download Vagrant insecure private key if necessary
function check_vagrant_private_key {
local key_name="vagrant"
local key_url=https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/$key_name
local vagrant_key_dir=$LIB_DIR/vagrant-ssh-keys
local vagrant_key_path=$vagrant_key_dir/$key_name
if [ ! -f "$vagrant_key_path" ]; then
download "$key_url" "$vagrant_key_dir" $key_name
fi
if ! ls -l "$vagrant_key_path"|grep -q "^-r--------"; then
echo "Adjusting permissions for $vagrant_key_path"
chmod 400 "$vagrant_key_path"
fi
}
function strip_top_dir {
local full_path=$1
echo "${full_path/$TOP_DIR\//}"
}
# Copy files or directories to VM (incl. implied directories; HOME is TOP_DIR)
function vm_scp_to_vm {
local ssh_port=$1
shift
check_vagrant_private_key
while (($#)); do
local src_path=$1
shift
local target_path=$(strip_top_dir "$src_path")
local target_dir=$(dirname "$target_path")
vm_ssh "$ssh_port" "mkdir -p $target_dir"
scp -q -r \
-i "$LIB_DIR/vagrant-ssh-keys/vagrant" \
-o "UserKnownHostsFile /dev/null" \
-o "StrictHostKeyChecking no" \
-P "$ssh_port" \
"$src_path" "$VM_SHELL_USER@localhost:$target_path"
done
}
# Execute commands via ssh
function vm_ssh {
local ssh_port=$1
shift
check_vagrant_private_key
ssh -q \
-i "$LIB_DIR/vagrant-ssh-keys/vagrant" \
-o "UserKnownHostsFile /dev/null" \
-o "StrictHostKeyChecking no" \
-p "$ssh_port" \
"$VM_SHELL_USER@localhost" "$@"
}
function wait_for_ssh {
local ssh_port=$1
echo "Waiting for ssh server to respond on local port $ssh_port"
while [ : ]; do
if vm_ssh "$ssh_port" exit ; then
break
else
echo -n .
sleep 1
fi
done
}
# Copy one script to VM and execute it via ssh; log output to separate file
function ssh_exec_script {
local ssh_port=$1
local script_path=$2
vm_scp_to_vm "$ssh_port" "$script_path"
local remote_path=$(strip_top_dir "$script_path")
echo -en "\n$(date) start $remote_path"
local script_name="$(basename "$script_path" .sh)"
local prefix=$(get_next_prefix "$LOG_DIR" "auto")
local log_path=$LOG_DIR/${prefix}_${script_name}.auto
local rc=0
vm_ssh "$ssh_port" "bash $remote_path && rm -vf $remote_path" \
> "$log_path" 2>&1 || rc=$?
if [ $rc -ne 0 ]; then
echo >&2
echo "ERROR: ssh returned status $rc for $remote_path" |
tee >&2 -a "$LOG_DIR/error.log"
# kill osbash host scripts
kill -- -$$
fi
echo -en "\n$(date) done"
}
# Wait for sshd, prepare autostart dirs, and execute autostart scripts on VM
function ssh_process_autostart {
local ssh_port=$1
wait_for_ssh "$ssh_port"
vm_ssh "$ssh_port" "rm -rf lib config autostart"
vm_scp_to_vm "$ssh_port" "$TOP_DIR/lib" "$TOP_DIR/config"
local script_path=""
for script_path in "$AUTOSTART_DIR/"*.sh; do
ssh_exec_script "$ssh_port" "$script_path"
rm -f "$script_path" >&2
done
touch "$STATUS_DIR/done"
}
#-------------------------------------------------------------------------------
# Autostart mechanism
#-------------------------------------------------------------------------------
function autostart_reset {
clean_dir "$AUTOSTART_DIR"
clean_dir "$STATUS_DIR"
}
function process_begin_files {
local processing=("$STATUS_DIR"/*.sh.begin)
if [ -n "${processing[0]-}" ]; then
local file
for file in "${processing[@]}"; do
echo >&2 -en "\nVM processing $(basename "$file" .begin)"
rm "$file"
done
fi
}
# Wait until all autofiles are processed (indicated by a "$STATUS_DIR/done"
# file created either by osbashauto or ssh_process_autostart)
function wait_for_autofiles {
shopt -s nullglob
${WBATCH:-:} wbatch_wait_auto
# Remove autostart files and return if we are just faking it for wbatch
${OSBASH:+:} autostart_reset
${OSBASH:+:} return 0
until [ -f "$STATUS_DIR/done" -o -f "$STATUS_DIR/error" ]; do
# Note: begin files (created by indicate_current_auto) are only visible
# if the STATUS_DIR directory is shared between host and VM
${WBATCH:-:} process_begin_files
echo >&2 -n .
sleep 1
done
# Check for remaining *.sh.begin files
${WBATCH:-:} process_begin_files
if [ -f "$STATUS_DIR/done" ]; then
rm "$STATUS_DIR/done"
else
echo -e >&2 "\nERROR occured. Exiting."
kill -- -$$
fi
echo
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Prepending numbers ensures scripts will be executed in the order they
# were added to the queue.
function _autostart_queue {
local src_dir=${1%/*}
# if src_dir is a code, turn it into a real path
src_dir="$(src_dir_code_to_dir "$src_dir")"
local src_name=${1##*/}
# If we get a target name, file will be renamed
local target_name=${2:-$src_name}
if [[ $target_name = *.sh ]]; then
# Create target file name like 01_apt_init.sh
local prefix=$(get_next_prefix "$AUTOSTART_DIR" "sh" 2)
target_name="${prefix}_$target_name"
fi
if [ "$src_name" = "$target_name" ]; then
echo >&2 -e "\t$src_name"
else
echo >&2 -e "\t$src_name -> $target_name"
fi
local src_path=$src_dir/$src_name
cp -- "$src_path" "$AUTOSTART_DIR/$target_name"
${WBATCH:-:} wbatch_cp_auto "$src_path" "$AUTOSTART_DIR/$target_name"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Print to the console which file requested guest scripts to run
function log_autostart_source {
# If the caller doesn't provide a config file, log the caller's source file
local src_file=${1:-${BASH_SOURCE[1]##*/}}
echo >&2 "Copying autostart files set in $src_file"
}
# autostart <src_dir|src_dir_code> <file> <new_name>
# e.g. autostart osbash init_xxx_node.sh init_controller_node.sh
function autostart_and_rename {
local src_dir=$1
local src_file=$2
local target_file=$3
# Don't log this file -- log our caller's source file
log_autostart_source "${BASH_SOURCE[1]##*/}"
_autostart_queue "$src_dir/$src_file" "$target_file"
}
# autostart <src_dir|src_dir_code> <file> [<file> ...]
# e.g. autostart scripts prepare_home.sh apt_init.sh
function autostart {
local src_dir=$1
shift
# Don't log this file -- log our caller's source file
log_autostart_source "${BASH_SOURCE[1]##*/}"
while (($#)); do
local src_file=$1
shift
_autostart_queue "$src_dir/$src_file"
done
}
# Parse command and arguments after a "cmd" token in config/scripts.*
function command_from_config {
local cmd_string=( $1 )
local cmd=${cmd_string[0]}
local vm_name=$NODE_NAME
case "$cmd" in
boot)
# Boot with queued autostart files now, wait for shutdown
echo >&2 _vbox_boot_with_autostart "$vm_name" "$VM_SSH_PORT"
_vbox_boot_with_autostart "$vm_name" "$VM_SSH_PORT"
;;
snapshot)
# Format: snapshot <snapshot_name>
local shot_name=${cmd_string[1]}
echo >&2 vm_snapshot "$vm_name" "$shot_name"
vm_snapshot "$vm_name" "$shot_name"
;;
wait_for_shutdown)
echo >&2 vm_wait_for_shutdown "$vm_name"
vm_wait_for_shutdown "$vm_name"
;;
snapshot_cycle)
# comprises shutdown, boot, wait_for_shutdown, snapshot
local shot_name=${cmd_string[1]}
echo >&2 snapshot_cycle "$vm_name" "$shot_name"
_autostart_queue "osbash/shutdown.sh"
_vbox_boot_with_autostart "$vm_name" "$VM_SSH_PORT"
vm_wait_for_shutdown "$vm_name"
vm_snapshot "$vm_name" "$shot_name"
;;
init_node)
echo >&2 vm_init_node "$vm_name"
vm_init_node "$vm_name"
;;
*)
echo >&2 "Error: invalid cmd: $cmd"
exit 1
;;
esac
}
# Parse config/scripts.* configuration files
function autostart_from_config {
local config_file=$1
local config_path=$CONFIG_DIR/$config_file
if [ ! -f "$config_path" ]; then
echo >&2 "Config file not found: $config_file"
return 1
fi
log_autostart_source "$config_file"
# Open file on file descriptor 3 so programs we call in this loop (ssh)
# are free to mess with the standard file descriptors.
exec 3< "$config_path"
while read -r field_1 field_2 <&3; do
if [[ $field_1 =~ ^# ]]; then
# Skip lines that are commented out
continue
elif [ "$field_1" == "cmd" ]; then
command_from_config "$field_2"
else
# Queue a script for autostart
# Format: <dircode> <script_name>
# Note: _autostart_queue takes care of dircode
echo >&2 _autostart_queue "$field_1/$field_2"
_autostart_queue "$field_1/$field_2"
fi
done
}
#-------------------------------------------------------------------------------
# Functions to get install ISO images
#-------------------------------------------------------------------------------
function download {
local url=$1
local dest_dir=$2
local dest_file=$3
local rc=0
local wget_exe=$(which wget)
mkdir -pv "$dest_dir"
if [ -n "$wget_exe" ]; then
$wget_exe --output-document "$dest_dir/$dest_file" "$url"||rc=$?
else
# Mac OS X has curl instead of wget
local curl_exe=$(which curl)
if [ -n "$curl_exe" ]; then
$curl_exe "$url" -o "$dest_dir/$dest_file"||rc=$?
fi
fi
if [ $rc -ne 0 ]; then
echo >&2 "Unable to download $url, quitting."
exit 1
fi
}
function get_iso_name {
basename "${ISO_URL:-}"
}
function find_install-iso {
local iso_name=$1
if [ ! -f "$ISO_DIR/$iso_name" ]; then
echo >&2 "$iso_name not in $ISO_DIR; downloading"
download "$ISO_URL" "$ISO_DIR" "$iso_name"
fi
}
# vim: set ai ts=4 sw=4 et ft=sh: