# Author: william2003[at]gmail[dot]com
# Created Date: 11/17/2008
# http://engineering.ucsb.edu/~duonglt/vmware/
# http://communities.vmware.com/docs/DOC-8760
##################################################################

LAST_MODIFIED_DATE=11/14/2009

# directory that all VM backups should go (e.g. /vmfs/volumes/SAN_LUN1/mybackupdir)
VM_BACKUP_VOLUME=/backup/melina

# Format output of VMDK backup
# zeroedthick
# 2gbsparse
# thin
# eagerzeroedthick
DISK_BACKUP_FORMAT=thin

# Number of backups for a given VM before deleting
VM_BACKUP_ROTATION_COUNT=3

# Shutdown guestOS prior to running backups and power them back on afterwards
# This feature assumes VMware Tools are installed, else they will not power down and loop forever
# 1=on, 0 =off
POWER_VM_DOWN_BEFORE_BACKUP=0

# enable shutdown code 1=on, 0 = off
ENABLE_HARD_POWER_OFF=0

# if the above flag "ENABLE_HARD_POWER_OFF "is set to 1, then will look at this flag which is the # of iterations
# the script will wait before executing a hard power off, this will be a multiple of 60seconds 
# (e.g) = 3, which means this will wait up to 180seconds (3min) before it just powers off the VM
ITER_TO_WAIT_SHUTDOWN=3

# Number of iterations the script will wait before giving up on powering down the VM and ignoring it for backup
# this will be a multiple of 60 (e.g) = 5, which means this will wait up to 300secs (5min) before it gives up
POWER_DOWN_TIMEOUT=5

# enable compression with gzip+tar 1=on, 0=off
ENABLE_COMPRESSION=0

############################
####### NEW PARAMS #########
############################

# Disk adapter type: buslogic, lsilogic or ide
ADAPTER_FORMAT=buslogic

# Include VMs memory when taking snapshot
VM_SNAPSHOT_MEMORY=1

# Quiesce VM when taking snapshot (requires VMware Tools to be installed)
VM_SNAPSHOT_QUIESCE=0

##########################################################
# NON-PERSISTENT NFS-BACKUP ONLY
# 
# ENABLE NON PERSISTENT NFS BACKUP 1=on, 0=off

ENABLE_NON_PERSISTENT_NFS=0

# umount NFS datastore after backup is complete 1=yes, 0=no
UNMOUNT_NFS=0

# IP Address of NFS Server
NFS_SERVER=172.30.0.195

# Path of exported folder residing on NFS Server (e.g. /some/mount/point )
NFS_MOUNT=/nfsshare

# Non-persistent NFS datastore display name of choice
NFS_LOCAL_NAME=nfs_storage_backup

# Name of backup directory for VMs residing on the NFS volume
NFS_VM_BACKUP_DIR=mybackups 

########################## DO NOT MODIFY PAST THIS LINE ##########################

LOG_LEVEL="info"
VMDK_FILES_TO_BACKUP="all"
# default 15min timeout
SNAPSHOT_TIMEOUT=15

# Directory naming convention for backup rotations (please ensure there are no spaces!)
VM_BACKUP_DIR_NAMING_CONVENTION="$(date +%F)"


printUsage() {
        echo "###############################################################################"
        echo "#"
        echo "# ghettoVCB for ESX/ESXi 3.5 & 4.x+"
        echo "# Author: William Lam"
        echo "# http://www.engineering.ucsb.edu/~duonglt/vmware/"
        echo "# Created: 11/17/2008"
        echo "# Last modified: ${LAST_MODIFIED_DATE}"
        echo "#"
        echo "###############################################################################"
        echo
        echo "Usage: $0 -f [VM_BACKUP_UP_LIST] -c [VM_CONFIG_DIR] -l [LOG_FILE]"
        echo
        echo "OPTIONS:"
        echo "   -f     List of VMs to backup"
	echo "   -c     Configuration directory for VM backups"
        echo "   -l     File to output logging"
	echo "   -d     Debug level [info|debug|dryrun] (default: info)"
        echo
        echo "(e.g.)"
        echo -e "\nBackup VMs stored in a list"
        echo -e "\t$0 -f vms_to_backup"
	echo -e "\nBackup VMs based on specific configuration located in directory"
        echo -e "\t$0 -f vms_to_backup -c vm_backup_configs"
        echo -e "\nOutput will log to /tmp/ghettoVCB.log"
        echo -e "\t$0 -f vms_to_backup -l /tmp/ghettoVCB.log"
	echo -e "\nDry run (no backup will take place)"
        echo -e "\t$0 -f vms_to_backup -d dryrun"
        echo
        exit 1
}

logger() {
	LOG_TYPE=$1
        MSG=$2

	if [[ "${LOG_LEVEL}" == "debug" ]] && [[ "${LOG_TYPE}" == "debug" ]] || [[ "${LOG_TYPE}" == "info" ]] || [[ "${LOG_TYPE}" == "dryrun" ]]; then
		TIME=$(date +%F" "%H:%M:%S)
       		if [ "${LOG_TO_STDOUT}" -eq 1 ]; then
               		echo -e "${TIME} -- ${LOG_TYPE}: ${MSG}"
        	else
       	        	echo -e "${TIME} -- ${LOG_TYPE}: ${MSG}" >> "${LOG_OUTPUT}"
       		fi
	fi
}

sanityCheck() {
        NUM_OF_ARGS=$1

	if [[ ${NUM_OF_ARGS} -ne 0 ]] && [[ ${NUM_OF_ARGS} -ne 2 ]] && [[ ${NUM_OF_ARGS} -ne 4 ]] && [[ ${NUM_OF_ARGS} -ne 6 ]] && [[ ${NUM_OF_ARGS} -ne 8 ]]; then
                printUsage
        fi

	if [[ ! -f "${VM_FILE}" ]] && [[ "${USE_CONF}" -eq 0 ]]; then
		echo -e "Error: \"${VM_FILE}\" is not valid file!\n"
		printUsage
	fi

 	if [[ ! -d "${CONFIG_DIR}" ]] && [[ "${USE_CONF}" -eq 1 ]]; then
		echo -e "Error: \"${CONFIG_DIR}\" is not valid directory!\n"
                printUsage
       	fi

	#log to stdout or to logfile
        if [ -z "${LOG_OUTPUT}" ]; then
                LOG_TO_STDOUT=1
                REDIRECT=/dev/null
        else
                LOG_TO_STDOUT=0
                REDIRECT=${LOG_OUTPUT}
                echo "Logging output to \"${LOG_OUTPUT}\" ..."
                touch "${LOG_OUTPUT}"
        fi

	if [ -f /usr/bin/vmware-vim-cmd ]; then
                VMWARE_CMD=/usr/bin/vmware-vim-cmd
                VMKFSTOOLS_CMD=/usr/sbin/vmkfstools
        elif [ -f /bin/vim-cmd ]; then
                VMWARE_CMD=/bin/vim-cmd
                VMKFSTOOLS_CMD=/sbin/vmkfstools
        else
                logger "info" "ERROR: Unable to locate *vimsh*! You're not running ESX(i) 3.5+ or 4.0+!"
                echo "ERROR: Unable to locate *vimsh*! You're not running ESX(i) 3.5+ or 4.0+!"
                exit
        fi

        ESX_VERSION=$(vmware -v | awk '{print $3}')
        if [ "${ESX_VERSION}" == "4.0.0" ]; then
                VER=4
        else
                ESX_VERSION=$(vmware -v | awk '{print $4}')
                if [[ "${ESX_VERSION}" == "3.5.0" ]] || [[ "${ESX_VERSION}" == "3i" ]]; then
                        VER=3
                else
                        echo "You're not running ESX(i) 3.5+ or 4.0+!"
                        exit
                fi
        fi

        if [ ${ENABLE_NON_PERSISTENT_NFS} -eq 1 ]; then
                ${VMWARE_CMD} hostsvc/summary/fsvolume | awk '{print $1'} | grep "\"${NFS_LOCAL_NAME}\"" > /dev/null 2>&1
                if [ ! $? -eq 0 ]; then
                        #1 = readonly
                        #0 = readwrite
                        ${VMWARE_CMD} hostsvc/datastore/nas_create "${NFS_LOCAL_NAME}" "${NFS_SERVER}" "${NFS_MOUNT}" 0
                fi
        fi

        if [ ! "`whoami`" == "root" ]; then
                logger "info" "This script needs to be executed by \"root\"!"
		echo "ERROR: This script needs to be executed by \"root\"!"
                exit 1
        fi
}

startTimer() {
        START_TIME=$(date)
        S_TIME=$(date +%s)
}

endTimer() {
        END_TIME=$(date)
        E_TIME=$(date +%s)
        DURATION=$(echo $((E_TIME - S_TIME)))

        #calculate overall completion time
        if [ ${DURATION} -le 60 ]; then
                logger "info" "Backup Duration: ${DURATION} Seconds"
        else
                logger "info" "Backup Duration: $(awk 'BEGIN{ printf "%.2f\n", '${DURATION}'/60}') Minutes"
        fi
}

captureDefaultConfigurations() {
	DEFAULT_VM_BACKUP_VOLUME="${VM_BACKUP_VOLUME}"
	DEFAULT_DISK_BACKUP_FORMAT="${DISK_BACKUP_FORMAT}"
	DEFAULT_VM_BACKUP_ROTATION_COUNT="${VM_BACKUP_ROTATION_COUNT}"
	DEFAULT_POWER_VM_DOWN_BEFORE_BACKUP="${POWER_VM_DOWN_BEFORE_BACKUP}"
	DEFAULT_ENABLE_HARD_POWER_OFF="${ENABLE_HARD_POWER_OFF}"
	DEFAULT_ITER_TO_WAIT_SHUTDOWN="${ITER_TO_WAIT_SHUTDOWN}"
	DEFAULT_POWER_DOWN_TIMEOUT="${POWER_DOWN_TIMEOUT}"
	DEFAULT_SNAPSHOT_TIMEOUT="${SNAPSHOT_TIMEOUT}"
	DEFAULT_ENABLE_COMPRESSION="${ENABLE_COMPRESSION}"
	DEFAULT_ADAPTER_FORMAT="${ADAPTER_FORMAT}"
	DEFAULT_VM_SNAPSHOT_MEMORY="${VM_SNAPSHOT_MEMORY}"
	DEFAULT_VM_SNAPSHOT_QUIESCE="${VM_SNAPSHOT_QUIESCE}"
	DEFAULT_VMDK_FILES_TO_BACKUP="${VMDK_FILES_TO_BACKUP}"
}

useDefaultConfigurations() {
	VM_BACKUP_VOLUME="${DEFAULT_VM_BACKUP_VOLUME}"
	DISK_BACKUP_FORMAT="${DEFAULT_DISK_BACKUP_FORMAT}"
	VM_BACKUP_ROTATION_COUNT="${DEFAULT_VM_BACKUP_ROTATION_COUNT}"
	POWER_VM_DOWN_BEFORE_BACKUP="${DEFAULT_POWER_VM_DOWN_BEFORE_BACKUP}"
	ENABLE_HARD_POWER_OFF="${DEFAULT_ENABLE_HARD_POWER_OFF}"
	ITER_TO_WAIT_SHUTDOWN="${DEFAULT_ITER_TO_WAIT_SHUTDOWN}"
	POWER_DOWN_TIMEOUT="${DEFAULT_POWER_DOWN_TIMEOUT}"
	SNAPSHOT_TIMEOUT="${DEFAULT_SNAPSHOT_TIMEOUT}"
	ENABLE_COMPRESSION="${DEFAULT_ENABLE_COMPRESSION}"
	ADAPTER_FORMAT="${DEFAULT_ADAPTER_FORMAT}"
	VM_SNAPSHOT_MEMORY="${DEFAULT_VM_SNAPSHOT_MEMORY}"
	VM_SNAPSHOT_QUIESCE="${DEFAULT_VM_SNAPSHOT_QUIESCE}"
	VMDK_FILES_TO_BACKUP="all"
}

reConfigureBackupParam() {
        VM=$1

        if [ -e "${CONFIG_DIR}/${VM}" ]; then
                logger "info" "CONFIG - USING CONFIGURATION FILE = ${CONFIG_DIR}/${VM}"
                . "${CONFIG_DIR}/${VM}"
        else
                useDefaultConfigurations
        fi
}

dumpHostInfo() {
	logger "debug" "HOST BUILD: $(vmware -v)"
	logger "debug" "HOSTNAME: $(hostname)\n"
}

findVMDK() {
        VMDK_TO_SEARCH_FOR=$1

	if [ "${USE_CONF}" -eq 1 ]; then
		logger "debug" "findVMDK() - Searching for VMDK: \"${VMDK_TO_SEARCH_FOR}\" to backup"

		OLD_IFS2="${IFS}"
        	IFS=","
	        for k in ${VMDK_FILES_TO_BACKUP}
        	do
                	VMDK_FILE=$(echo $k | sed -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//')
		        if [ "${VMDK_FILE}" == "${VMDK_TO_SEARCH_FOR}" ]; then
				logger "debug" "findVMDK() - Found VMDK! - \"${VMDK_TO_SEARCH_FOR}\" to backup"
        	                isVMDKFound=1
			fi	
	        done
		IFS="{OLD_IFS2}"
	fi
}

getVMDKs() {
	#get all VMDKs listed in .vmx file
        VMDKS_FOUND=`grep -iE '(scsi|ide)' "${VMX_PATH}" | grep -i fileName | awk -F " " '{print $1}'`

        TMP_IFS=${IFS}
        IFS=${ORIG_IFS}
        #loop through each disk and verify that it's currently present and create array of valid VMDKS
        for DISK in ${VMDKS_FOUND};
        do
        	#extract the SCSI ID and use it to check for valid vmdk disk
                SCSI_ID=`echo ${DISK%%.*}`
                grep -i "${SCSI_ID}.present" "${VMX_PATH}" | grep -i "true" > /dev/null 2>&1
                #if valid, then we use the vmdk file
                if [ $? -eq 0 ]; then
			#verify disk is not independent
			grep -i "${SCSI_ID}.mode" "${VMX_PATH}" | grep -i "independent" > /dev/null 2>&1 
			if [ $? -eq 1 ]; then
	                	grep -i "${SCSI_ID}.deviceType" "${VMX_PATH}" | grep -i "scsi-hardDisk" > /dev/null 2>&1
        	                #if we find the device type is of scsi-disk, then proceed
                	        if [ $? -eq 0 ]; then
                        		DISK=`grep -i ${SCSI_ID}.fileName "${VMX_PATH}" | awk -F "\"" '{print $2}'`
                                	VMDKS="${DISK}:${VMDKS}"
	                        else
        	                        #if the deviceType is NULL for IDE which it is, thanks for the inconsistency VMware
                	                #we'll do one more level of verification by checking to see if an ext. of .vmdk exists
                        	        #since we can not rely on the deviceType showing "ide-hardDisk"
                                	grep -i ${SCSI_ID}.fileName "${VMX_PATH}" | grep -i ".vmdk" > /dev/null 2>&1
	                                if [ $? -eq 0 ]; then
        	                	        DISK=`grep -i ${SCSI_ID}.fileName "${VMX_PATH}" | awk -F "\"" '{print $2}'`
                	                        VMDKS="${DISK}:${VMDKS}"
                        	        fi
                        	fi
			fi
                fi
         done
         IFS=${TMP_IFS}
}

dumpVMConfigurations() {
	logger "info" "CONFIG - VM_BACKUP_VOLUME = ${VM_BACKUP_VOLUME}"
        logger "info" "CONFIG - VM_BACKUP_ROTATION_COUNT = ${VM_BACKUP_ROTATION_COUNT}"
        logger "info" "CONFIG - DISK_BACKUP_FORMAT = ${DISK_BACKUP_FORMAT}"
        logger "info" "CONFIG - ADAPTER_FORMAT = ${ADAPTER_FORMAT}"
        logger "info" "CONFIG - POWER_VM_DOWN_BEFORE_BACKUP = ${POWER_VM_DOWN_BEFORE_BACKUP}"
        logger "info" "CONFIG - ENABLE_HARD_POWER_OFF = ${ENABLE_HARD_POWER_OFF}"
	logger "info" "CONFIG - ITER_TO_WAIT_SHUTDOWN = ${ITER_TO_WAIT_SHUTDOWN}"
	logger "info" "CONFIG - POWER_DOWN_TIMEOUT = ${POWER_DOWN_TIMEOUT}"
	logger "info" "CONFIG - SNAPSHOT_TIMEOUT = ${SNAPSHOT_TIMEOUT}"
        logger "info" "CONFIG - LOG_LEVEL = ${LOG_LEVEL}"
	if [ -z "${LOG_OUTPUT}" ]; then
		logger "info" "CONFIG - BACKUP_LOG_OUTPUT = stdout"
	else
		logger "info" "CONFIG - BACKUP_LOG_OUTPUT = ${LOG_OUTPUT}"
	fi
        logger "info" "CONFIG - VM_SNAPSHOT_MEMORY = ${VM_SNAPSHOT_MEMORY}"
        logger "info" "CONFIG - VM_SNAPSHOT_QUIESCE = ${VM_SNAPSHOT_QUIESCE}"
        logger "info" "CONFIG - VMDK_FILES_TO_BACKUP = ${VMDK_FILES_TO_BACKUP}\n"
}

checkVMBackupRotation() {
	local BACKUP_DIR_PATH=$1
	local BACKUP_VM_NAMING_CONVENTION=$2
	LIST_BACKUPS=$(ls -tr "${BACKUP_DIR_PATH}")

	#default rotation if variable is not defined
	if [ -z ${VM_BACKUP_ROTATION_COUNT} ]; then
		VM_BACKUP_ROTATION_COUNT=1
	fi

	IFS='
'
	for DIR in ${LIST_BACKUPS};
	do
		TMP_DIR="${BACKUP_DIR_PATH}/${DIR}"
		TMP=$(echo ${TMP_DIR##*--})

	        if [ ${TMP} = "${BACKUP_VM_NAMING_CONVENTION}" ]; then
	                NEW=${TMP}--1
	                mv "${BACKUP_DIR_PATH}/${DIR}" "${NEW}"
	        elif [ $TMP -ge ${VM_BACKUP_ROTATION_COUNT} ]; then
	                rm -rf "${BACKUP_DIR_PATH}/${DIR}"
	        else
			BASE=$(echo ${TMP_DIR%--*})
	                NEW=${BASE}--$((${TMP}+1))
	                mv "${BACKUP_DIR_PATH}/${DIR}" "$NEW"
	        fi
	done	
	unset IFS
}

checkVMBackupRotationWCompresssion() {
        local BACKUP_DIR_PATH=$1
        local BACKUP_VM_NAMING_CONVENTION=$2
        LIST_BACKUPS=$(ls -tr "${BACKUP_DIR_PATH}")

        #default rotation if variable is not defined
        if [ -z ${VM_BACKUP_ROTATION_COUNT} ]; then
                VM_BACKUP_ROTATION_COUNT=1
        fi

        IFS='
'
        for DIR in ${LIST_BACKUPS};
        do
                TMP_DIR="${BACKUP_DIR_PATH}/${DIR}"
                TMP_ZIP=$(echo ${TMP_DIR%*.gz})
                TMP=$(echo ${TMP_ZIP##*--})

                if [ "${TMP}.gz" = "${BACKUP_VM_NAMING_CONVENTION}" ]; then
                        NEW=${TMP}--1
                        mv "${BACKUP_DIR_PATH}/${DIR}" "${NEW}.gz"
                elif [ $TMP -ge ${VM_BACKUP_ROTATION_COUNT} ]; then
                        rm "${BACKUP_DIR_PATH}/${DIR}"
                else
                        BASE=$(echo ${TMP_ZIP%--*})
                        NEW=${BASE}--$((${TMP}+1))
                        mv "${BACKUP_DIR_PATH}/${DIR}" "$NEW.gz"
                fi
        done
        unset IFS
}

ghettoVCB() {
	VM_INPUT=$1

	captureDefaultConfigurations

	dumpHostInfo	

	if [ "${USE_CONF}" -eq 0 ]; then
		dumpVMConfigurations
	fi

	#dump out all virtual machines allowing for spaces now
	${VMWARE_CMD} vmsvc/getallvms | sed 's/[[:blank:]]\{3,\}/   /g' | awk -F'   ' '{print "\""$1"\";\""$2"\";\""$3"\""}' |  sed 's/\] /\]\";\"/g' | sed '1,1d' > /tmp/vms_list

	ORIG_IFS=${IFS}
	IFS='
'
	for VM_NAME in `cat "${VM_INPUT}" | grep -v "#" | sed '/^$/d' | sed -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//'`;
        do
		VM_ID=`grep -E "\"${VM_NAME}\"" /tmp/vms_list | awk -F ";" '{print $1}' | sed 's/"//g'`

		if [[ "${USE_CONF}" -eq 1 ]] && [[ ! -z ${VM_ID} ]]; then
                        reConfigureBackupParam "${VM_NAME}"
                        dumpVMConfigurations
                fi

		#ensure default value if one is not selected or variable is null
		if [ -z ${VM_BACKUP_DIR_NAMING_CONVENTION} ]; then
			VM_BACKUP_DIR_NAMING_CONVENTION="$(date +%F)"
		fi

		VMFS_VOLUME=`grep -E "\"${VM_NAME}\"" /tmp/vms_list | awk -F ";" '{print $3}' | sed 's/\[//;s/\]//;s/"//g'`
		VMX_CONF=`grep -E "\"${VM_NAME}\"" /tmp/vms_list | awk -F ";" '{print $4}' | sed 's/\[//;s/\]//;s/"//g'`
		VMX_PATH="/vmfs/volumes/${VMFS_VOLUME}/${VMX_CONF}"
		VMX_DIR=`dirname "${VMX_PATH}"`

		#checks to see if we can pull out the VM_ID
		if [ -z ${VM_ID} ]; then
			logger "info" "Error: failed to locate and extract VM_ID for ${VM_NAME}!\n"

		elif [ "${LOG_LEVEL}" == "dryrun" ]; then
			logger "dryrun" "###############################################"
			logger "dryrun" "Virtual Machine: $VM_NAME"
			logger "dryrun" "VM_ID: $VM_ID"
			logger "dryrun" "VMX_PATH: $VMX_PATH"
			logger "dryrun" "VMX_DIR: $VMX_DIR"
			logger "dryrun" "VMX_CONF: $VMX_CONF"
			logger "dryrun" "VMFS_VOLUME: $VMFS_VOLUME"
			logger "dryrun" "VMDK(s): "
			getVMDKs
			OLD_IFS="${IFS}"
                        IFS=":"
                        for j in ${VMDKS};
                        do
				logger "dryrun" "\t${j}"
			done
			IFS="${OLD_IFS}"
			VMDKS=""	
			logger "dryrun" "###############################################\n"

                #checks to see if the VM has any snapshots to start with
                elif ls "${VMX_DIR}" | grep -q delta > /dev/null 2>&1; then
	                logger "info" "Snapshot found for ${VM_NAME}, backup will not take place\n"

                elif [[ -f "${VMX_PATH}" ]] && [[ ! -z "${VMX_PATH}" ]]; then
		 	#nfs case and backup to root path of your NFS mount 	
	                if [ ${ENABLE_NON_PERSISTENT_NFS} -eq 1 ] ; then 
	                	BACKUP_DIR="/vmfs/volumes/${NFS_LOCAL_NAME}/${NFS_VM_BACKUP_DIR}/${VM_NAME}"
                                if [[ -z ${VM_NAME} ]] || [[ -z ${NFS_LOCAL_NAME} ]] || [[ -z ${NFS_VM_BACKUP_DIR} ]]; then
                                        logger "info" "Variable BACKUP_DIR was not set properly, please ensure all required variables for non-persistent NFS backup option has been defined"
                                        exit 1
                                fi

	              	#non-nfs (SAN,LOCAL)  	
	                else
	                	BACKUP_DIR="${VM_BACKUP_VOLUME}/${VM_NAME}"
                                if [[ -z ${VM_BACKUP_VOLUME} ]]; then
                                        logger "info" "Variable VM_BACKUP_DIR was not defined"
                                        exit 1
                                fi
	                fi

			#initial root VM backup directory
			if [ ! -d "${BACKUP_DIR}" ]; then
				mkdir -p "${BACKUP_DIR}"
				if [ ! -d "${BACKUP_DIR}" ]; then
					logger "info" "Unable to create \"${BACKUP_DIR}\"! - Ensure VM_BACKUP_VOLUME was defined correctly"
					exit 1
				fi
	                fi

			# directory name of the individual Virtual Machine backup followed by naming convention followed by count
			VM_BACKUP_DIR="${BACKUP_DIR}/${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}"
			mkdir -p "${VM_BACKUP_DIR}"

			cp "${VMX_PATH}" "${VM_BACKUP_DIR}"

			#extract all valid VMDK(s) from VM
			getVMDKs

			ORGINAL_VM_POWER_STATE=$(${VMWARE_CMD} vmsvc/power.getstate ${VM_ID} | tail -1)
			CONTINUE_TO_BACKUP=1	
	
			#section that will power down a VM prior to taking a snapshot and backup and power it back on
			if [ ${POWER_VM_DOWN_BEFORE_BACKUP} -eq 1 ]; then
				START_ITERATION=0
				logger "info" "Powering off initiated for ${VM_NAME}, backup will not begin until VM is off..."

				${VMWARE_CMD} vmsvc/power.shutdown ${VM_ID} > /dev/null 2>&1
				while ${VMWARE_CMD} vmsvc/power.getstate ${VM_ID} | grep -i "Powered on" > /dev/null 2>&1;
				do
					#enable hard power off code
					if [ ${ENABLE_HARD_POWER_OFF} -eq 1 ]; then
						if [ ${START_ITERATION} -ge ${ITER_TO_WAIT_SHUTDOWN} ]; then
							logger "info" "Hard power off occured for ${VM_NAME}, waited for $((ITER_TO_WAIT_SHUTDOWN*60)) seconds" 
							${VMWARE_CMD} vmsvc/power.off ${VM_ID} > /dev/null 2>&1 
							#this is needed for ESXi, even the hard power off did not take affect right away
							sleep 60
							break
						fi
					fi

					logger "info" "VM is still on - Iteration: ${START_ITERATION} - sleeping for 60secs (Duration: $((START_ITERATION*60)) seconds)"
        	                        sleep 60

					#logic to not backup this VM if unable to shutdown
					#after certain timeout period
					if [ ${START_ITERATION} -ge ${POWER_DOWN_TIMEOUT} ]; then
						logger "info" "Unable to power off ${VM_NAME}, waited for $((POWER_DOWN_TIMEOUT*60)) seconds! Ignoring ${VM_NAME} for backup!"
						CONTINUE_TO_BACKUP=0
						break
					fi
					START_ITERATION=$((START_ITERATION + 1))
				done
				if [ ${CONTINUE_TO_BACKUP} -eq 1 ]; then
					logger "info" "VM is powerdOff"
				fi
			fi

			if [ ${CONTINUE_TO_BACKUP} -eq 1 ]; then 
				logger "info" "Initiate backup for ${VM_NAME}"
				startTimer

				SNAP_SUCCESS=1

				#powered on VMs only
                        	if [[ ! ${POWER_VM_DOWN_BEFORE_BACKUP} -eq 1 ]] && [[ "${ORGINAL_VM_POWER_STATE}" != "Powered off" ]]; then
					SNAPSHOT_NAME="ghettoVCB-snapshot-$(date +%F)"
					logger "info" "Creating Snapshot \"${SNAPSHOT_NAME}\" for ${VM_NAME}"
        	                        ${VMWARE_CMD} vmsvc/snapshot.create ${VM_ID} "${SNAPSHOT_NAME}" "${SNAPSHOT_NAME}" "${VM_SNAPSHOT_MEMORY}" "${VM_SNAPSHOT_QUIESCE}" > /dev/null 2>&1

					logger "debug" "Waiting for snapshot \"${SNAPSHOT_NAME}\" to be created"
					logger "debug" "Snapshot timeout set to: $((SNAPSHOT_TIMEOUT*60)) seconds"
                                        START_ITERATION=0
					while [ $(${VMWARE_CMD} vmsvc/snapshot.get ${VM_ID} | wc -l) -eq 1 ]
					do
						if [ ${START_ITERATION} -ge ${SNAPSHOT_TIMEOUT} ]; then
							logger "info" "Snapshot timed out, failed to create snapshot: \"${SNAPSHOT_NAME}\" for ${VM_NAME}"
							SNAP_SUCCESS=0
							break
						fi

						logger "debug" "Waiting for snapshot creation to be completed - Iteration: ${START_ITERATION} - sleeping for 60secs (Duration: $((START_ITERATION*30)) seconds)"
                                                sleep 60
						
						START_ITERATION=$((START_ITERATION + 1))
					done
                	        fi

				if [ ${SNAP_SUCCESS} -eq 1 ]; then 
					OLD_IFS="${IFS}"
					IFS=":"
					for j in ${VMDKS};
					do
						VMDK="${j}"
						isVMDKFound=0
		
						findVMDK "${VMDK}"

						if [[ $isVMDKFound -eq 1 ]] || [[ "${VMDK_FILES_TO_BACKUP}" == "all" ]]; then 
							#added this section to handle VMDK(s) stored in different datastore than the VM
							echo ${VMDK} | grep "^/vmfs/volumes" > /dev/null 2>&1
							if [ $? -eq 0 ]; then
								SOURCE_VMDK="${VMDK}"
								DS_UUID="$(echo ${VMDK#/vmfs/volumes/*})"
								DS_UUID="$(echo ${DS_UUID%/*/*})"
								VMDK_DISK="$(echo ${VMDK##/*/})"
								mkdir -p "${VM_BACKUP_DIR}/${DS_UUID}"
								DESTINATION_VMDK="${VM_BACKUP_DIR}/${DS_UUID}/${VMDK_DISK}"
							else
								SOURCE_VMDK="${VMX_DIR}/${VMDK}"
								DESTINATION_VMDK="${VM_BACKUP_DIR}/${VMDK}"
							fi
	
							#support for vRDM and deny pRDM
							grep "vmfsPassthroughRawDeviceMap" "${SOURCE_VMDK}" > /dev/null 2>&1
							if [ $? -eq 1 ]; then
								if [ "${DISK_BACKUP_FORMAT}" == "zeroedthick" ]; then
									if [ "${VER}" == "4" ]; then 
                		        	        		       	${VMKFSTOOLS_CMD} -i "${SOURCE_VMDK}" -a "${ADAPTER_FORMAT}" -d zeroedthick "${DESTINATION_VMDK}" 2>&1 | tee -a -i "${REDIRECT}"
									else 
										${VMKFSTOOLS_CMD} -i "${SOURCE_VMDK}" -a "${ADAPTER_FORMAT}" "${DESTINATION_VMDK}" 2>&1 | tee -a -i "${REDIRECT}"
									fi
		        		        	        elif [ "${DISK_BACKUP_FORMAT}" == "2gbsparse" ]; then
        		        		        	        ${VMKFSTOOLS_CMD} -i "${SOURCE_VMDK}" -a "${ADAPTER_FORMAT}" -d 2gbsparse "${DESTINATION_VMDK}" 2>&1 | tee -a -i "${REDIRECT}"
		                		        	elif [ "${DISK_BACKUP_FORMAT}" == "thin" ]; then
        		                		        	${VMKFSTOOLS_CMD} -i "${SOURCE_VMDK}" -a "${ADAPTER_FORMAT}" -d thin "${DESTINATION_VMDK}" 2>&1 | tee -a -i "${REDIRECT}"
				                	        elif [ "${DISK_BACKUP_FORMAT}" == "eagerzeroedthick" ]; then
									if [ "${VER}" == "4" ]; then
                	    				    		        ${VMKFSTOOLS_CMD} -i "${SOURCE_VMDK}" -a "${ADAPTER_FORMAT}" -d eagerzeroedthick "${DESTINATION_VMDK}" 2>&1 | tee -a -i "${REDIRECT}"
                        	        				else
                                	        				${VMKFSTOOLS_CMD} -i "${SOURCE_VMDK}" -a "${ADAPTER_FORMAT}" "${DESTINATION_VMDK}" 2>&1 | tee -a -i "${REDIRECT}"
	                                        			fi
	        	                        		fi
							else
                	        	        		logger "info" "WARNING: A physical RDM \"${SOURCE_VMDK}\" was found for ${VM_NAME}, which will not be backed up"
                        				fi
						fi
					done
					IFS="${OLD_IFS}"
				fi

				#powered on VMs only w/snapshots
                        	if [[ ${SNAP_SUCCESS} -eq 1 ]] && [[ ! ${POWER_VM_DOWN_BEFORE_BACKUP} -eq 1 ]] && [[ "${ORGINAL_VM_POWER_STATE}" == "Powered on" ]] || [[ "${ORGINAL_VM_POWER_STATE}" == "Suspended" ]]; then
                                	${VMWARE_CMD} vmsvc/snapshot.remove ${VM_ID} > /dev/null 2>&1

	                                #do not continue until all snapshots have been committed
        	                        logger "info" "Removing snapshot from ${VM_NAME} ..."
                	                while ls "${VMX_DIR}" | grep -q delta;
                        	        do
                                	        sleep 5
	                                done
        	                fi

				if [[ ${POWER_VM_DOWN_BEFORE_BACKUP} -eq 1 ]] && [[ "${ORGINAL_VM_POWER_STATE}" == "Powered on" ]]; then
        	                        #power on vm that was powered off prior to backup
                	                logger "info" "Powering back on ${VM_NAME}"
                        	        ${VMWARE_CMD} vmsvc/power.on ${VM_ID} > /dev/null 2>&1
	                        fi

				TMP_IFS=${IFS}
                	        IFS=${ORIG_IFS}
	                        if [ ${ENABLE_COMPRESSION} -eq 1 ]; then
        	                        logger "info" "Compressing VM backup \"${BACKUP_DIR}/${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}.gz\"..."
					if [ ${IS_4I} -eq 1 ]; then
						busybox tar -cz -C "${BACKUP_DIR}" "${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}" -f "${BACKUP_DIR}/${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}.gz"
					else 
						tar -cz -C "${BACKUP_DIR}" "${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}" -f "${BACKUP_DIR}/${VM_NAME}-${VM_BACKUP_DIR_NAMING_CONVENTION}.gz"
					fi
                	                rm -rf "${VM_BACKUP_DIR}"
                        	        checkVMBackupRotationWCompresssion "${BACKUP_DIR}" "${VM_BACKUP_DIR}.gz"
	                        else
        	                        checkVMBackupRotation "${BACKUP_DIR}" "${VM_BACKUP_DIR}"
                	        fi
                        	IFS=${TMP_IFS}
	                        VMDKS=""

				endTimer
				if [ ${SNAP_SUCCESS} -eq 1 ]; then
					logger "info" "Successfully completed backup for ${VM_NAME}!\n"
				else 
					logger "info" "Error: Unable to backup ${VM_NAME} due to snapshot creation!\n"
				fi
	                else
				if [ ${CONTINUE_TO_BACKUP} -eq 0 ]; then
					logger "info" "Error: Failed to backup ${VM_NAME}!\n"
				else
        	                	logger "info" "Error: Failed to lookup ${VM_NAME}!\n"
				fi
	                fi	
		fi
        done
	unset IFS

        if [[ ${ENABLE_NON_PERSISTENT_NFS} -eq 1 ]] && [[ ${UNMOUNT_NFS} -eq 1 ]] ; then
		${VMWARE_CMD} hostsvc/datastore/destroy ${NFS_LOCAL_NAME}	
	fi
}

####################
#		   #
# Start of Script  #
#		   #
####################

IS_4I=0

if [ ! -f /bin/bash ]; then
	IS_4I=1
fi

USE_CONF=0
BACKUP_ALL_VMS=0

#read user input
while getopts ":f:c:l:d:" ARGS; do
        case $ARGS in
                f)      VM_FILE="${OPTARG}"
                        ;;
		c)
			CONFIG_DIR="${OPTARG}"
			USE_CONF=1
			;;
                l)
                        LOG_OUTPUT="${OPTARG}"
                        ;;
                d)
                        LOG_LEVEL="${OPTARG}"
                        ;;
                :)
                        echo "Option -${OPTARG} requires an argument."
                        exit 1
                        ;;
                *)
                        usage
                        exit 1
                        ;;
        esac
done

# Overwrite VM_FILE
/usr/bin/vmware-vim-cmd  vmsvc/getallvms | sed 's/[[:blank:]]\{3,\}/   /g' | awk -F'   ' '{print $2}' | sed '1,1d' >/tmp/vm_backup_list
VM_FILE='/tmp/vm_backup_list'




#performs a check on the number of commandline arguments + verifies $2 is a valid file
sanityCheck $# 

logger "info" "============================== ghettoVCB LOG START ==============================\n"
ghettoVCB ${VM_FILE}
logger "info" "============================== ghettoVCB LOG END ================================\n"

