# 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_name { echo "base-$VM_ACCESS-$DISTRO.vdi" } function get_base_disk_path { echo "$DISK_DIR/$(get_base_disk_name)" } #------------------------------------------------------------------------------- # 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 # 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 [ ...] # 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 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: # 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: