#!/usr/bin/ksh93

#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# =============================================================================
# create_vm : Create and configure a Virtual Machine
# =============================================================================
#

#
# Establish PATH for non-built in commands
#
export PATH=/usr/xpg4/bin:/bin:/usr/bin:/usr/sbin

#
# Source in shared common VMC code.
# All variable and functions defined in vmc_common are named with
# prifix "vmc" or VMC when possible.
#
. /usr/share/distro_const/vmc/vmc_common

#
# Make sure all math stuff runs in the "C" locale
#
vmc_set_locale

#######################################################################
# cleanup
#	This function attempts to clean up any resources this script
#	could generate. Depending on where in the script this function
#	is involved some resources may not be there to cleanup, but
#	that will not adversely effect anything.
#
#	This function is not defined using the function keyword
#	to avoid an exit loop.
#
# Input: none
# Returns: none
#
#######################################################################
cleanup ()
{
	#
	# It is not necessary to process errors.
	#
	{

		trap "" ERR INT
		set +o errexit

		#
		# detach the VDI disk from the VM:
		#
		VBoxManage -q modifyvm ${DIST_NAME} --hda none

		#
		# unregister and delete the VM:
		#
		VBoxManage -q unregistervm ${DIST_NAME} --delete

		#
		# grab the location of the VDI file so we can delete it later
		#
		VDI="$(VBoxManage -q showhdinfo ${DIST_NAME} | \
		    grep "^Location" | awk '{ print $2 }')"

		#
		# after the VM has been unregister and delete close
		# the dvd and disk medium
		#
		VBoxManage -q closemedium dvd ${VMC_NEW_ISO}
		VBoxManage -q closemedium disk ${DIST_NAME}

		#
		# delete the VDI:
		#
		rm -rf "${VDI}"

	} > /dev/null 2>&1
}

#######################################################################
# main
#
# Name: create_vm
#
# Args:
#	MFEST_SOCKET: Socket to get manifest data via ManifestRead object
#	PKG_IMG_PATH: Package image area
#	TMP_DIR: Temporary directory to contain the bootroot file
#	BR_BUILD: Area where bootroot is put together (not used)
#	MEDIA_DIR: Area where the media is put. (Not used)
#
# Additional arguments to this finalizer script passed from the VMC Manifest:
#	<DISK_SIZE> - Size (in MB) of the VM Virtual Disk
#	<RAM_SIZE> - Size (in MB) of the VM RAM
#
# Inputs to be taken from the VMC Manifest:
#	<DIST_NAME> - "name" tag
#
# Output:
#
#	No formal output. (Result is a created VM instance.)
#
# Diagnostic Output:
#
#	Message written to standard output (print -u1) will go to
#	the distro constructor log file.
#
#	Message written to standard error (print -u2) will go to
#	the distro constructor log file and the screen where the
#	distro constructor is being run.
#
# Description:
#
#	This finalizer script will create the VM based on input from
#	the VMC manifest. The status of each VBox command will be
#	validated. If an error is detected a meaningful error message
#	will be written to the DC log.
#
# Returns:
#	1 on failure
#	0 on success
#
#######################################################################

builtin cp
builtin date
builtin mkdir
builtin mv
builtin rm

#
# Variable which act as constants are in CAPs.
#     Some constants are not read-only because they need to be altered
#     in order to be correctly established.
#
# Variable which can be modified are in lower case
#
typeset -i cmdsts
typeset    vbox_cmd
typeset -r MANIFEST_READ=/usr/bin/ManifestRead

#
# Constants passed to modifyvm
#
typeset -r  BOOT1="dvd"
typeset -r  NIC1="nat"
typeset -r  NICTYPE1="82540EM"
typeset -ri VRAM=12

#
# Process command line arguments
#
if (( $# != 8 )) ; then
	print -u2 "\nWrong number of arguments provided by Distro Constructor."
	print -u2 "The arguments \"disk size\" \"ram size\" and " \
	    "\"OS type\" must be specified in the manifest."
	exit 1
fi

typeset -r MFEST_SOCKET="$1"
typeset -r PKG_IMG_PATH="$2"

#
# A non-integer value accidently passed by the user would be converted
# to "0" when assigned to an interger type.
#
# Expected integer arguements are assigned to temp. non-integer variables
# so validation can be done in the validation section and the error message
# can reflect what the user passed instead of "0".
#
typeset -r TMP_DISK_SIZE=$6 # VM Disk size
typeset -r TMP_RAM_SIZE=$7  # VM RAM size

#
# -l convert to lowercase to facilitate validation.
#
typeset -rl OSTYPE=$8 # The virtual machine OS type


#
# Read the distribution name tag from the manifest 
#
typeset -r DIST_NAME=$(${MANIFEST_READ} ${MFEST_SOCKET} "name")

#
# Validate the arguments
#

if [[ ! -d ${PKG_IMG_PATH} ]] ; then
	print -u2 "\nImage package area ${PKG_IMG_PATH} does not exist."
	exit 1
fi

#
# If the argument is numeric assign its value to DISK_SIZE
#
if [[ ${TMP_DISK_SIZE} != +([0-9]) ]]
then
	print -u2 "\nImproper argument."
	print -u2 "\nThe disk size of ${TMP_DISK_SIZE} is invalid."
	print -u2 "\"disk size\" must be an integer, min \"12000\" (MB)."
	exit 1
fi
typeset -i DISK_SIZE=${TMP_DISK_SIZE} # VM Disk size

if ((${DISK_SIZE} < 12000 || ${DISK_SIZE} > 99999999)) ; then
        print -u2 "\nThe disk size of ${DISK_SIZE} is invalid. It must" \
            " be in range [12000, 99999999] (MB)"
	exit 1
fi

#
# If the argument is numeric assign its value to RAM_SIZE
#
if [[ ${TMP_RAM_SIZE} != +([0-9]) ]]
then
	print -u2 "\nImproper argument."
	print -u2 "\nThe ram size of ${TMP_RAM_SIZE} is invalid."
	print -u2 "\"ram size\" must be an integer, min \"1000\" (MB)."
	exit 1
fi
typeset -i RAM_SIZE=${TMP_RAM_SIZE}  # VM RAM size

if ((${RAM_SIZE} < 1000 || ${RAM_SIZE} > 16384)) ; then
        print -u2 "\nThe ram size of ${RAM_SIZE} is invalid. It must" \
            " be in range [1000, 16384] (MB)"
        exit 1
fi

if [[ "${OSTYPE}" != "openindiana" && "${OSTYPE}" != "openindiana_64" ]]; then
	print -u2 "\nThe OS Type specified: ${OSTYPE} is invalid." \
	    "It must be either \"openindiana\"  or \"openindiana_64\" "
	exit 1
fi

if [[ "XX${DIST_NAME}" == "XX" ]] ; then
	print -u2 "\nThe distribution name ${DIST_NAME}" \
	    "is not valid.  It cannot be empty."
	exit 1
fi

#
# Set up error handling.
# Where possible explicitly check command return status for errors.
#
trap "vmc_error_handler_trap" INT
set +o errexit

#
# Set the path the the new modified ISO image This is done in
# vmc_common function so it is set in one place.
#
vmc_set_new_iso_path ${PKG_IMG_PATH}
cmdsts=$?
vmc_error_handler ${cmdsts} "\nError: ISO image ${VMC_NEW_ISO} not found."

#
# Check if Virtual Box is installed
#
vmc_is_vbox_installed
cmdsts=$?
vmc_error_handler ${cmdsts} \
    "\nError: Required VirtualBox version is not installed"

#
# Check if the new VM already exists. If it does clean it up
# before proceeding.
#
if [[ $(VBoxManage -q list vms | grep -c ^\"${DIST_NAME}\") != 0 ]] ; then
	print -u2 "\nError: VM ${DIST_NAME} already exists." \
	    "\n\tThe existing VM ${DIST_NAME} will be removed and a new" \
	    "\n\tversion will be created."
	cleanup
fi

#
# Create the VM
#
vbox_cmd="VBoxManage -q createvm --name ${DIST_NAME} --register"
print -u1 "\nInvoking: ${vbox_cmd}"
${vbox_cmd}
cmdsts=$?
vmc_error_handler ${cmdsts} "\nError: ${vbox_cmd} failed"

#
# Create the virtual hard disk
#
vbox_cmd="VBoxManage -q createhd --filename ${DIST_NAME} \
    --size ${DISK_SIZE}  --remember"
print -u1 "\nInvoking: ${vbox_cmd}"
${vbox_cmd}
cmdsts=$?
vmc_error_handler ${cmdsts} "\nError: ${vbox_cmd} failed"

#
# Register the ISO with Virtual Box
#
vbox_cmd="VBoxManage -q openmedium dvd ${VMC_NEW_ISO}"
print -u1 "\nInvoking: ${vbox_cmd}"
${vbox_cmd}
cmdsts=$?
vmc_error_handler ${cmdsts} "\nError: ${vbox_cmd} failed"

#
# Configure Virtual Machine to boot from ISO
#
vbox_cmd="VBoxManage -q modifyvm ${DIST_NAME} --ostype ${OSTYPE} \
    --memory ${RAM_SIZE} --nic1 ${NIC1} --nictype1 ${NICTYPE1} \
    --hda ${DIST_NAME} --vram ${VRAM} --dvd ${VMC_NEW_ISO} --boot1 ${BOOT1}"
print -u1 "\nInvoking: ${vbox_cmd}"
${vbox_cmd}
cmdsts=$?
vmc_error_handler ${cmdsts} "\nError: ${vbox_cmd} failed"

#
# exit with success status
#
exit 0

