#!/bin/bash
#
# Copyright 2005-2007 VMware, Inc. All rights reserved.
#
# This script will import all virtual machines from a specified target
# directory.

vcb_libs=/usr/lib/vmware/vcb
hostd_libs=/usr/lib/vmware/hostd
cfg=${VCB_CONFIG_FILE:-"/etc/vmware/backuptools.conf"}

. "$vcb_libs/plugins/lib"
if [ -e "$cfg" ] ; then
    . "$cfg"
fi

export HOST_THUMBPRINT

# Temporary files used by this script. Will be cleaned up on exit
tmp=${TEMPDIR:-"/tmp"}

#
# Exit codes used by this script
#
ERR_OK=0
ERR_NOTALLVMSRESTORED=1
ERR_NOVMSFOUND=2
ERR_CANNOTLIST=3
ERR_USAGE=5
ERR_CRED=6

# Override application name displayed by vcbRestore
export VCB_APPNAME=vcbResAll

# Temporary files used by this script. Will be cleaned up on exit
VMLIST_FILE=/tmp/vcbRes-$$-vmlist
LOGDIR=/var/log/vmware/vcbResAll-$$
IMPORT_DELAY=0



#
# Description:
# Sanity check arguments. -- We could leave this to vcbMounter,
# but then we will get error-reporting on a per-VM basis, which
# is more confusing to the end user, so do it here.
#
# Arguments: 
# None. Checks global variables.
# 
# Result:
# Returns ERR_OK on success or ERR_USAGE on failure.
#
function init
{
    local rv=$ERR_OK
    local msg=""

    if [ -z "$VCHOST" ] ; then
	msg="Host name is unset"
    fi
    
    if [ -z "$USERNAME" ] ; then
	msg="VC user name is unset"
    fi
    
    if [ -z "$SOURCEDIR" ] ; then
	msg="Source directory is unset"
    fi

    if [ -z "$msg" ] ; then 
        if [ -z "${INTERCEPT_p}" ] ; then
            if [ "${INTERCEPT_p+EMPTY}" = "EMPTY" ] ; then
                PASSWORD="${INTERCEPT_p}"
            else
                if [ -z "${PASSWORD}" ] ; then
                    if [ "${PASSWORD+EMPTY}" != "EMPTY" ] ; then
                        read -s -p "Password:" PASSWORD
	                echo
                    fi
                fi
            fi
        else
            PASSWORD="${INTERCEPT_p}"
        fi
        if [ -z "$PASSWORD" ] ; then
            if [ "${PASSWORD+EMPTY}" != "EMPTY" ] ; then
	        msg="VC user password is unset"
            fi
        fi
    fi

    if [ -z "$msg" ] ; then 
	rm -fr "$VMLIST_FILE" "$LOGDIR"
	mkdir "$LOGDIR"
	rv=$ERR_OK
    else
	echo "Missing or illegal arguments: ${msg}."
	vcbRestore
	echo ""
	echo "vcbResAll adds the \"-d\" option to this to specify an optional "
	echo "delay between virtual machine restores. For example, \"-d 30\" will"
	echo "add a delay of 30 seconds between each VM to be restored."
	rv=$ERR_USAGE
    fi

    return $rv
}



#
# Description:
# Writes a list of all virtual machines that can be found to stdout.
# Essentially it lists all the subdirectories in the source directory.
#
# Arguments:
# $1: Source directory containing virtual machines to restore.
#
# Return:
# Returns ERR_OK on success or ERR_NOVMSFOUND if no subdirectories
# were found in the specified directory. ERR_CANNOTLIST is returned
# if the transport plugin specified does not support the listvms
# operation.
#
function create_vm_list 
{
    local sourcedir="$1"
    local tmpfile="${tmp}"/vcbRes-$$
    local sane_path
    local prefix
    local rv

    LD_LIBRARY_PATH="$LD_LIBRARY_PATH":"$hostd_libs" \
    VCB_PASSWORD="$PASSWORD" \
        "$vcb_libs/vcbVmName" -h "$VCHOST" -u "$USERNAME" \
            -s  any: > "$tmpfile"
    rv=$?
    if [ "$rv" != "0" ] ; then
        echo `grep "Error:" "$tmpfile"` | sed "s_vcbVmName_vcbResAll_" >&2
        rm -f "$tmpfile"
        return $ERR_CRED
    fi
    rm -f "$tmpfile"

    prefix=`check_transport_plugin "$SOURCEDIR"`
    rv=$?
    if [ "$rv" != "0" ] ; then
	return $rv
    fi
    load_transport_plugin "$prefix"
    sane_path=`remove_transport_prefix "$SOURCEDIR"`
    ${prefix}_listvms "${sane_path}"
    rv=$?
    if [ "$rv" = "0" ] ; then
	return $ERR_OK
    elif [ "$rv" = "1" ] ; then
	return $ERR_NOVMSFOUND
    fi
    return $ERR_CANNOTLIST
}



#
# Description:
# Read a list of directories containing virtual machine backups to be
# restored and try to restore all of them.
#
# Arguments:
# None. Uses global option variables.
#
# Return:
# Returns ERR_OK when all VMs could be restored or ERR_NOTALLVMSRESTORED
# otherwise.
#
function restore_vms
{
    local ds_arg=""
    local starttime=`date`
    local rv=$ERR_OK
    local total
    local ok
    local failed
    declare -i total=0 ok=0 failed=0

    if [ -n "$DATASTORE" ] ; then
	ds_arg="-C \"$DATASTORE\""
    fi

    echo "Per VM log files will be in ${LOGDIR}." 
    while read VMDIR
    do
	local catalog_opt=""

	echo -n `date`": Importing VM "${VMDIR}"..."
	detaillog="$LOGDIR"/"$VMDIR"
	if [ -n "$ALTERNATE_CATALOGFILE" ] ; then
	    catalog_opt="-a \"${SOURCEDIR}/${VMDIR}/${ALTERNATE_CATALOGFILE}\" "
	fi

	eval VCB_PASSWORD='"$PASSWORD"' \
	    vcbRestore -h '"$VCHOST"' -u '"$USERNAME"' \
	    -s '"${SOURCEDIR}"'/'"${VMDIR}"' $ds_arg $catalog_opt "$UNPARSED_ARGUMENTS" \
	    < /dev/tty | tee -a "${detaillog}"-running.log 2>&1
	rv=${PIPESTATUS[0]}
	if [ $rv != $ERR_OK ] ; then
	    echo FAILED
	    rv=$ERR_NOTALLVMSRESTORED
	    failed=failed+1
	    mv "${detaillog}"-running.log "${detaillog}"-failed.log
	else
	    echo SUCCEEDED
	    ok=ok+1
	    mv "${detaillog}"-running.log "${detaillog}"-ok.log
	    sleep $IMPORT_DELAY
	fi
	total=total+1
    done
   
    echo ========================================================
    echo "Restore start time : " $starttime
    echo "Restored end time  : " `date`
    echo
    echo "       Total number of VMs processed: " $total
    echo " Number of VMs restored successfully: " $ok
    echo "                Number of VMS failed: " $failed
    echo ========================================================
    return $rv
}



#
# Description:
# Check for non-zero return status of a command. If a non-zero return
# status is encountered, clean up temp files, print an error message and
# exit with an appropriate error code.
#
# Arguments:
# $1: Return value to evaluate. Should be one of the ERR_ constants defined
#     in this script
#
# Result:
# None. Will terminate the shell script in case of an error.
#
function error_exit
{
    local retval="$1"
    local msg

    if [ "$retval" != "$ERR_OK" ] ; then
	case "$retval" in
        "$ERR_CRED")
            msg="Invalid  user credentials."
            ;;
	"$ERR_NOTALLVMRESTORED")
	    msg="Restore did not succeed for all virtual machines."
	    ;;
	"$ERR_NOVMSFOUND")
	    msg="No virtual machines could be found in the source directory."
	    ;;
	"$ERR_CANNOTLIST")
	    msg="Transport plugin used does not support listing virtual machine backups."
	    ;;
	"$ERR_USAGE")
	    msg="Script was invoked with illegal or missing arguments."
	    ;;
	*)
	    msg="Unspecified error during backup. Please refer to logs."
	    ;;
	esac
	echo ${msg} >&2
	rm -f "$VMLIST_FILE"
	exit $retval
    fi
}



# We intercept some parameters here, even tho we only modify
# the search specifier and the dest. dir. Doing so will allow
# us to print more useful error messages.
intercept_arguments "h:u:p:s:C:d:a:" "$@"
if [ -n "${INTERCEPT_h}" ] ; then 
    VCHOST="${INTERCEPT_h}"
fi
if [ -n "${INTERCEPT_u}" ] ; then 
    USERNAME="${INTERCEPT_u}"
fi
if [ -n "${INTERCEPT_C}" ] ; then 
    DATASTORE="${INTERCEPT_C}"
fi
if [ -n "${INTERCEPT_d}" ] ; then 
    IMPORT_DELAY="${INTERCEPT_d}"
fi
if [ -n "${INTERCEPT_a}" ] ; then
    rv=$ERR_USAGE
    error_exit $rv
#    ALTERNATE_CATALOGFILE="${INTERCEPT_a}"
fi
SOURCEDIR="${INTERCEPT_s}"
init
rv=$?
error_exit $rv

create_vm_list "$SOURCEDIR" > "$VMLIST_FILE"
rv=$?
error_exit $rv

cat "$VMLIST_FILE" | restore_vms
rv=$?
rm -f "$VMLIST_FILE"
exit $rv
