#!/bin/bash
# Etersoft, 2010
# 2010 (c) Devaev Maxim <mdevaev@etersoft.ru>
#
# vboxmachines - init script for vms autorun on boot
#
#####

# chkconfig: 235 98 55
# description: VirtualBox machines auto start
#
### BEGIN INIT INFO
# Provides:       vboxdrv
# Required-Start: $syslog
# Required-Stop:
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Short-Description: VirtualBox machines auto start
### END INIT INFO

WITHOUT_RC_COMPAT="1"
. /etc/rc.d/init.d/functions


#####
. /etc/vbox/vboxmachines.conf
. /etc/vbox/vbox.conf


#####
get_vm_name_by_uuid()
{
	su "$VBOX_USER" -l -c "VBoxManage --nologo showvminfo \"$1\"" | grep 'Name:' | sed -e 's/^Name:\s*//g'
}

get_necessary_vms_uuids_list()
{
	local group="${1-}"
	local list="$VBOX_MACHINES_LIST"
	[ -z "$group" ] || list="$VBOX_MACHINES_GROUPS_DIR/$group.list"
	cat "$list" | sed -e '/^#/d' | sed -e '/^$/d' | sort
}

get_vm_groups_list()
{
	find "$VBOX_MACHINES_GROUPS_DIR" -name '*.list' | sed -e 's/\([^\/]*\/\)*\(.*\)\.list$/\2/g'
}

get_running_vms_uuids_list()
{
	su "$VBOX_USER" -l -c "VBoxManage --nologo list runningvms | grep -o \{.*\}" | sed -e 's/{\(.*\)}/\1/' | sort
}

###
start_vm_by_uuid()
{
	local vm_uuid="$1"
	echo "Starting \""`get_vm_name_by_uuid "$vm_uuid"`"\" - $vm_uuid ..."
	su "$VBOX_USER" -l -c "VBoxManage --nologo startvm \"$vm_uuid\" --type headless"
}

stop_vm_by_uuid()
{
	local retcode=
	local vm_uuid="$1"
	local action="${2-$VBOX_SHUTDOWN_ACTION}"
	local timeout="${3-$VBOX_SHUTDOWN_TIMEOUT}"
	echo -n "Stopping \""`get_vm_name_by_uuid "$vm_uuid"`"\" - $vm_uuid: "
	su "$VBOX_USER" -l -c "VBoxManage --nologo controlvm \"$vm_uuid\" \"$action\""
	retcode="$?"

	[ "$timeout" -ne "0" ] || return "$retcode"

	local wait_count="0"
	while [ ! -z `get_running_vms_uuids_list | grep "$vm_uuid"` ]; do
		echo -n "."
		sleep 1

		let "wait_count+=1"
		if [ $wait_count -gt "$timeout" ]; then
			echo -n "Save state due timed out: "
			su "$VBOX_USER" -l -c "VBoxManage --nologo controlvm \"$vm_uuid\" savestate"
			retcode="$?"
			break
		fi
	done
	[ "$wait_count" -ne "0" ] && echo
	return "$retcode"
}

#####
start_necessary_vms()
{
	local retcode="0"
	local vm_group="$1"; shift
	local script_dir="$VBOX_MACHINES_SCRIPTS_DIR"
	[ -z "$vm_group" ] || script_dir="$VBOX_MACHINES_SCRIPTS_DIR/$vm_group"
	get_necessary_vms_uuids_list "$vm_group" | while read vm_uuid; do
		ExecIfExecutable "$script_dir/vmstart-pre" $vm_uuid "`get_vm_name_by_uuid "$vm_uuid"`"
		start_vm_by_uuid "$vm_uuid" "$@"
		if [ "$?" -ne "0" ]; then
			retcode="1"
		else
			ExecIfExecutable "$script_dir/vmstart-post" $vm_uuid "`get_vm_name_by_uuid "$vm_uuid"`"
		fi
	done
	return "$retcode"
}

stop_necessary_vms()
{
	local retcode="0"
	local vm_group="$1"; shift
	local script_dir="$VBOX_MACHINES_SCRIPTS_DIR"
	[ -z "$vm_group" ] || script_dir="$VBOX_MACHINES_SCRIPTS_DIR/$vm_group"
	get_necessary_vms_uuids_list "$vm_group" | while read vm_uuid; do
		ExecIfExecutable "$script_dir/vmstop-pre" $vm_uuid "`get_vm_name_by_uuid "$vm_uuid"`"
		stop_vm_by_uuid "$vm_uuid" "$@"
		if [ "$?" -ne "0" ]; then
			retcode="1"
		else
			ExecIfExecutable "$script_dir/vmstop-post" $vm_uuid "`get_vm_name_by_uuid "$vm_uuid"`"
		fi
	done
	return "$retcode"
}

restart_necessary_vms()
{
	stop_necessary_vms
	start_necessary_vms
}

status_necessary_vms()
{
	local vm_group="$1"
	running_vms_uuids_list=`get_running_vms_uuids_list`
	necessary_vms_uuids_list=`get_necessary_vms_uuids_list "$vm_group"`

	echo "$necessary_vms_uuids_list" | while read vm_uuid; do
		if [ -z `echo "$running_vms_uuids_list" | grep "$vm_uuid"` ]; then
			echo "[   ---   ] :: \""`get_vm_name_by_uuid "$vm_uuid"`"\" - $vm_uuid"
		else
			echo "[ RUNNING ] :: \""`get_vm_name_by_uuid "$vm_uuid"`"\" - $vm_uuid"
		fi
	done
}

get_list_vms()
{
	dhcp_info=`ssh -T dhcpinfo@$DHCP_HOST -p $DHCP_SSH_PORT \
		-o "StrictHostKeyChecking no" -i /etc/vbox/dhcpinfo.key | grep "# virtualbox systems"`
		vms_uuids_list=`VBoxManage --nologo list vms | grep -o '\{.*\}' | sed -e 's/{\(.*\)}/\1/'`
		for vms_uuids_list_item in $vms_uuids_list; do
			vm_info=`VBoxManage --nologo showvminfo "$vms_uuids_list_item"`
			vm_state=`echo "$vm_info" | grep 'State:' | head -n 1 | sed -e 's/^State:\s*\([^(]*\)\s(.*/\1/g' | grep -v 'powered off' `
			vm_name=`echo "$vm_info" | grep 'Name:' | head -n 1 | sed -e 's/^Name:\s*//g'`
			vm_mac=`echo "$vm_info" | grep -o '[0-9A-F]\{12\}' | head -n 1 | sed -e 's/\([0-9A-F][0-9A-F]\)/\1:/g' | sed -e 's/:$//'`
			if [ ! -z "$vm_mac" ]; then
				vm_hostname=`echo "$dhcp_info" | grep -i "$vm_mac" | awk '{print \$2}'`
				vm_ip=`echo "$dhcp_info" | grep -i "$vm_mac" | awk '{print \$8}' | sed -e 's/;$//'`
			fi
			if [ -z "$vm_ip" ]; then
				vm_ip=`VBoxManage guestproperty enumerate "$vms_uuids_list_item" | grep "V4/IP" | cut -d"," -f2 | cut -d":" -f2 | tr -d " "`
			fi
			printf "|| %10s | %30s | %36s | %30s | %15s ||\n" "$vm_state" "$vm_name" \
				"$vms_uuids_list_item" "$vm_hostname" "$vm_ip"
		done
}

#####
start_group()
{
	local retcode="0"
	local vm_group="$1"
	local script_dir="$VBOX_MACHINES_SCRIPTS_DIR/$vm_group"
	echo "* Group '$vm_group':"
	ExecIfExecutable "$script_dir/groupstart-pre" "$vm_group"
	start_necessary_vms "$vm_group"
	if [ "$?" -ne "0" ]; then
		retcode="1"
	else
		ExecIfExecutable "$script_dir/groupstart-post" "$vm_group"
	fi
	return "$retcode"
}

stop_group()
{
	local retcode="0"
	local vm_group="$1"
	local script_dir="$VBOX_MACHINES_SCRIPTS_DIR/$vm_group"

	local group_config="$VBOX_MACHINES_GROUPS_DIR/$vm_group.conf"
	local default_shutdown_action="$VBOX_SHUTDOWN_ACTION"
	local shutdown_action="$(unset VBOX_SHUTDOWN_ACTION &&
	    [ -s "$group_config" ] && . "$group_config";
	    printf %s "${VBOX_SHUTDOWN_ACTION-$default_shutdown_action}")"
	local default_shutdown_timeout="$VBOX_SHUTDOWN_TIMEOUT"
	local shutdown_timeout="$(unset VBOX_SHUTDOWN_TIMEOUT &&
	    [ -s "$group_config" ] && . "$group_config";
	    printf %s "${VBOX_SHUTDOWN_TIMEOUT-$default_shutdown_timeout}")"

	echo "* Group '$vm_group':"
	ExecIfExecutable "$script_dir/groupstop-pre" "$vm_group"
	stop_necessary_vms "$vm_group" "$shutdown_action" "$shutdown_timeout"
	if [ "$?" -ne "0" ]; then
		retcode="2"
	else
		ExecIfExecutable "$script_dir/groupstop-post" "$vm_group"
	fi
	return "$retcode"
}

status_group()
{
	local vm_group="$1"
	echo "* Group '$vm_group':"
	status_necessary_vms "$vm_group"
}


#####
start_vms()
{
	local retcode="0"
	local vm_group="${1-}"
	if [ -n "$vm_group" ]; then
		start_group "$vm_group" || retcode="1"
		return "$retcode"
	fi
	echo "* Global group:"
	start_necessary_vms || retcode="1"
	get_vm_groups_list | while read vm_group; do
		echo
		start_group "$vm_group" || retcode="2"
	done
	return "$retcode"
}

stop_vms()
{
	local retcode="0"
	local vm_group="${1-}"
	if [ -n "$vm_group" ]; then
		stop_group "$vm_group" || retcode="1"
		return "$retcode"
	fi
	echo "* Global group:"
	stop_necessary_vms || retcode="1"
	get_vm_groups_list | while read vm_group; do
		echo
		start_group "$vm_group" || retcode="2"
	done
	return "$retcode"
}

restart_vms()
{
	local vm_group="${1-}"
	stop_vms "$vm_group"
	start_vms "$vm_group"
}

status_vms()
{
	local vm_group="${1-}"
	if [ -n "$vm_group" ]; then
		status_group "$vm_group"
		return
	fi
	echo "* Global group:"
	status_necessary_vms
	get_vm_groups_list | while read vm_group; do
		echo
		status_group "$vm_group"
	done
}


#####
case "$1" in
	"start") start_vms "$2";;
	"stop") stop_vms "$2";;
	"restart") restart_vms "$2";;
	"status") status_vms "$2";;
	"get_list") get_list_vms;;
	*) msg_usage "${0##*/} {start|stop|restart|status}"
	exit 1
esac

exit "$?"

