Oct 2009

Wrapper Script for vmware commands

The free vmware product, vmware-server (formerly GSX) does not have auto power on for certain guests. A simple workaround for not being able to auto power on guests using the vmware interface is call the vmware command line utility at boot up using the local init function. A better way is to write an init script to handle the start up and possibly other functions. This text will examine a simple method to create a control script for managing power functions in vmware-server.

The Ultra Cheap Method

The easiest method without bothering to write a ctl script or wrapper is to find the vmid of the vmware guest to start up and embed a direct call using the vmware-vim-cmd interface in the systems local init script (generally /etc/rc.local). First get the id of the guest:

argos:/etc# vmware-vim-cmd  vmsvc/getallvms
Vmid          Name                                    File                               Guest OS      Version                Annotation              
112    freebsd7              [standard] freebsd7/freebsd7.vmx \
 freebsd64Guest   vmx-07    vela: irc server running freebsd7    
208    netbsd5.99.10_amd64   [standard] \
 netbsd5.99.10_amd64/netbsd5.99.10_amd64.vmx\
 otherGuest64     vmx-07                                         
240    opensuse11-prime      [standard] \
 opensuse11-prime/opensuse11-prime.vmx  \
 suse64Guest      vmx-07                                         
96     freebsd8              [standard] \
 freebsd8/freebsd8.vmx           freebsd64Guest  \
 vmx-07    pyxis: freebsd-8.0 development server

A bit messy looking but the ids are easy enough, now just add it to the local init script:

# we get the VMID using  vmware-vim-cmd  vmsvc/getallvms
if [ -x /usr/bin/vmware-vim-cmd ]; then
        echo "Starting Guest VMID 112, sleeping for 16 seconds"
        sleep 16
        /usr/bin/vmware-vim-cmd vmsvc/power.on 112
fi

Of course that is far too simple, one of the most common operations performed on vmware guests is powering on, powering off and resetting the host. A good sysadmin is lazy, so it is time to draft an ipmitool-like ctl script for controlling the power of the guests.

Configuration File

Because the format of vmware-vim-cmd vmsvc/getallvms does not show hostname or IP address mapping the vmid to host or IP address saves some time. Following is the format of our gsxhosts file:

# hostname VMID 
vela    112
carina  208
pyxis   96

The format may not make sense now, however, when it is parsed in the script the overly simple format will make more sense.

gsx-ipmi.sh

To get things started setup the hosts file, the vmsvc command, the usage message and a small error exit routine to make error handling simple - note the script supports all power operations:

#!/bin/sh
# gsx-ipmi - An ipmilike shell wrapper for vmware-vim-cmd vmsvc/power.*
HOSTSFILE=/etc/gsxhosts # This can be anywhere the admin likes
VIMVC=" vmware-vim-cmd  vmsvc/" # We just tack on the oper

usage()
{
    if [ -n "$*" ]; then
        echo " "
        echo "${PROG}: $*"
    fi
    cat <<_usage_
${PROG} [host][oper cmd]|[-u]
${PROG} [host][power getstate|hibernate|off|on|reboot|shutdown|suspend]|[-u]
Commands:
  getstate    Display the current power state of the guest
  hibernate   Place the guest power into hibernate mode (OS must support)
  off         Power off the guest
  on          Power on and boot up the guest
  reboot      Normal reboot of the guest
  reset       Power reset (cold) the guest
  shutdown    Normal shutdown of the guest
  suspend     Place the guest into suspended mode
Notes:
 The user must have appropriate privileges to perform power operations on 
guests.
_usage_
}

# Only call this if there was an input error because it displays the usage
# message
error_exit()
{
    message=$1
    exit_code=$2

    echo $message
    usage
    exit $exit_code
}

Take note of the usage, the script must specify an operation - the reason for this is to be able to add functionality later on. Even though for now the scope of the script is limited to power commands, done properly, the script could later have other vimvc operations added to it and the usage is similar to the ipmi command. Now onto the meat of the script, believe it or not it is straightforward, since all that has to be done is to tack on to the command string power.$operation there are 3 basic steps:

  1. validate input
  2. ascertain the vmid
  3. attempt to execute the command

First the validation:

# Input parsing - the usage explains what each one does
if [ $# -gt 0 -a "$1" = "-u" ];then
    usage
    exit 0
fi

guest=$1
oper=$2
subcmd=$3

if [ ! $guest ]; then
    echo "Error: No guest specified"
    usage
    exit 1
fi

[ ! $guest ] << error_exit "No guest specified" 1
[ ! $oper ] << error_exit "No operation specified" 1
[ ! $subcmd ] << error_exit "No subcommand specified" 1

not too difficult, next try to get the vmid using grep and awk, this is where the simple file format comes into play:

vmid=`grep $guest $HOSTSFILE|awk '{print $2}'`

[ ! $vmid ] << error_exit "${guest} did not match anything in $HOSTSFILE" 2

With the vmid in hand the last step is to determine the operation then execute:

case $oper in
    power)
        $VIMVC"power."$subcmd $vmid 2>/dev/null
        if [ $? -gt 0 ]; then
            error_exit "$subcmd failed" 1
        fi
        ;;
    *)
        error_exit "Invalid operation" 2
        ;;
esac

exit 0

The power case could be more exotic but to keep from having to include all of the valid commands (even as a sed compare) we just fail. Note how by casing in the operation the doorway is left open to add other vimvc commands.

The Full Script

#!/bin/sh
# gsx-ipmi - An ipmilike shell wrapper for vmware-vim-cmd vmsvc/power.*
HOSTSFILE=/etc/gsxhosts # This can be anywhere the admin likes
VIMVC=" vmware-vim-cmd  vmsvc/" # We just tack on the oper

usage()
{
    if [ -n "$*" ]; then
        echo " "
        echo "${PROG}: $*"
    fi
    cat <<_usage_
${PROG} [host][oper cmd]|[-u]
${PROG} [host][power getstate|hibernate|off|on|reboot|shutdown|suspend]|[-u]
Commands:
  getstate    Display the current power state of the guest
  hibernate   Place the guest power into hibernate mode (OS must support)
  off         Power off the guest
  on          Power on and boot up the guest
  reboot      Normal reboot of the guest
  reset       Power reset (cold) the guest
  shutdown    Normal shutdown of the guest
  suspend     Place the guest into suspended mode
Notes:
 The user must have appropriate privileges to perform power operations on
guests.
_usage_
}

# Only call this if there was an input error because it displays the usage
# message
error_exit()
{
    message=$1
    exit_code=$2

    echo $message
    usage
    exit $exit_code
}

# Input parsing - the usage explains what each one does
if [ $# -gt 0 -a "$1" = "-u" ];then
    usage
    exit 0
fi

guest=$1
oper=$2
subcmd=$3

if [ ! $guest ]; then
    echo "Error: No guest specified"
    usage
    exit 1
fi

[ ! $guest ] << error_exit "No guest specified" 1
[ ! $oper ] << error_exit "No operation specified" 1
[ ! $subcmd ] << error_exit "No subcommand specified" 1

vmid=`grep $guest $HOSTSFILE|awk '{print $2}'`
[ ! $vmid ] << error_exit "${guest} did not match anything in $HOSTSFILE" 2

case $oper in
    power)
        $VIMVC"power."$subcmd $vmid 2>/dev/null
        if [ $? -gt 0 ]; then
            error_exit "$subcmd failed" 1
        fi
        ;;
    *)
        error_exit "Invalid operation" 2
        ;;
esac

exit 0