In 2008 I posted on the linux-ha ML a resource agent script for vmware
server 2 virtual machines. It has been slightly modified and shipped
with heartbeat and (now) pacemaker.
Actually the script is broken:
the script is named "vmware" while the "resource-agent name" is set to
"vmwarevm". They should match.
Besides that, even if all the "OCF_" stuff is correctly declared the
script always reports:
"WARNING: Could not find out virtual machine name"
That's because in the modified version the function "set_environment" is
not properly executed before actually starting and stopping resources.

Attached is the 0.2 version I actually use in production. It's called
"VMwareVM". If you're going to modify the script before including PLEASE
post it on the list before so I can check if it actually works or not.

Cheers
--
Cristian Mammoli
APRA SISTEMI srl

#!/bin/sh
#
# VMware OCF resource agent 
#
# Copyright (c) 2010 Apra Sistemi s.r.l.
#                    All Rights Reserved.
#
# Description:  Manages VMware server 2.0 virtual machines 
#               as High-Availability resources
#
#
# Author:       Cristian Mammoli <c.mammoli AT apra DOT it>
# License:      GNU General Public License (GPL)
# Copyright:    (C) 2010 Apra Sistemi s.r.l.
#
# See usage() function below for more details...
#
# OCF instance parameters:
#  * OCF_RESKEY_VMXPATH (mandatory, full path to the virtual machine vmx file)
#  * OCF_RESKEY_VIMSHBIN (mandatory, full path to th vmware-vim-cmd executable)
#
# Requirements/caveats:
#  * vmware-server 2.0 installed and autostarted on all nodes
#  * vmdk files must be in the same directory of the vmx file
#  * vmx filenames must be unique, even if stored in different directories
#  * Default_Action_Timeout stock value (20 sec) isn't enough if you are 
#    dealing with many virtual machines: raise it to something around 900 secs
#    or use operation attributes with the proposed values
#  * Moving a vm among nodes will cause its mac address to change: if you need
#    to preserve the mac address set it manually in the nic options
#  * The script should be able to deal with paths and filenames with spaces,
#    anyway try to avoid it
  
# Initialization
#################################################################

# Source ocf shell functions
. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs

# Basic variables configuration
#################################################################

# Path to the virtual machine configuration file
VMXPATH="$OCF_RESKEY_vmxpath"    

# Path to the vmware-vim-cmd executable
VIMSHBIN="$OCF_RESKEY_vimshbin"

# Global variables
VMXDIR=
RELVMXPATH=
VMID=
VM=
VMAUTOMSG=

# vmware-vim-cmd functions
#################################################################

# Get virtual machine vid
vmware_get_vid() {
  $VIMSHBIN vmsvc/getallvms 2>/dev/null \
         | awk '/\/'"$1"'/ {print $1}'
}

# Is the vm waiting for input after a migration?
vmware_uuid_alt() {
  $VIMSHBIN vmsvc/message $1 2>/dev/null \
         | awk /^msg.uuid.altered/
}

# Get message id
vmware_get_msgid() {
  $VIMSHBIN vmsvc/message $1 2>/dev/null \
         | awk '/^Virtual machine message/ {print $4}' \
         | awk -F : '{print $1}'
}

# Answers message
vmware_answer_msg() {
  $VIMSHBIN vmsvc/message $1 $2 $3 &> /dev/null
}

# Register a virtual machine
vmware_register_vm() {
  $VIMSHBIN solo/registervm '"'$1'"' &> /dev/null
}

# Unregister a virtual machine
vmware_unregister_vm() {
  $VIMSHBIN  vmsvc/unregister $1 &> /dev/null
}

# Start a virtual machine
vmware_poweron_vm() {
  $VIMSHBIN vmsvc/power.on $1 &> /dev/null
}

# Suspend a virtual machine
vmware_suspend_vm() {
  $VIMSHBIN vmsvc/power.suspend $1 &> /dev/null 
}

# Get virtual machine power state
vmware_get_status() {
  $VIMSHBIN vmsvc/power.getstate $1 2>/dev/null \
         | awk '/^Powered on/ || /^Powered off/ || /^Suspended/'
}

# Get vid of missing virtual machines
vmware_get_broken() {
  $VIMSHBIN vmsvc/getallvm 2>&1 \
         | awk -F \' '/^Skipping/ {print $2}'
}

# Variables depending on the above functions 
#################################################################

vmware_set_env() {  
  # Directory containing the virtual machine
  VMXDIR="`dirname "$VMXPATH"`" 

  # Basename of the configuration file 
  RELVMXPATH="`basename "$VMXPATH"`"                        

  # Vid of the virtual machine (can be empty if the vm is not registered)
  VMID=`vmware_get_vid "$RELVMXPATH"`

  # Power state of the virtual machine (can be empty if the vm is not 
registered)
  VMSTATE="`vmware_get_status $VMID`"

  # Virtual machine name
  VM="`awk -F '"' '/^displayName/ {print $2}' "$VMXPATH"`"

  # msg.autoAnswer value in config file
  VMAUTOMSG="`awk -F '"' '/^msg.autoAnswer/ {print toupper($2)}' "$VMXPATH"`"
}

# Main functions
#################################################################

# Print usage summary
vmware_usage() {
        cat <<END
usage: $0 {start|stop|status|monitor|meta-data|validate-all}

Expects to have a fully populated OCF RA-compliant environment set.
END
}

# Check for mandatory files presence and consistency
vmware_validate() {
  if [ -z "`pidof vmware-hostd`" ]; then
    ocf_log err "vmware-hostd is not running: aborting"
    exit $OCF_ERR_GENERIC
  fi

  if [ ! -x "$VIMSHBIN" ]; then
    ocf_log err "vmware-vim-cmd executable missing or not in path ($VIMSHBIN): 
aborting"
    exit $OCF_ERR_ARGS
  fi 

  if [ ! -f "$VMXPATH" ]; then
    ocf_log err "Specified vmx file ($VMXPATH) does not exist: aborting"
    exit $OCF_ERR_ARGS
  fi
  
  # Now we can safely setup variables...
  vmware_set_env 
   
  # ... and verify them 
  if [ -z "$VM" ]; then
    ocf_log err "Could not find out virtual machine name: aborting"
    exit $OCF_ERR_ARGS
  fi

  if [ "$VMAUTOMSG" != "TRUE" ]; then
    ocf_log warn "Please set msg.autoAnswer = \"TRUE\" in your config file"
  fi

  # $VMID and $VMSTATE are allowed to be empty in case we are validating a 
  # virtual machine which is not registered

  return $OCF_SUCCESS
}

# Start a virtual machine
vmware_start() {
  # Don't start a VM if it's already running
  if [ "$VMSTATE" = "Powered on" ]; then
    ocf_log info "Virtual machine $VM is already running"
    return $OCF_SUCCESS
  else
    # Removes stale lockfiles and missing virtual machines
    # in case of a crash.
    # Do not use with a clustered filesystem or you could
    # end up starting the same VM in more than one node
    ocf_log info "Removing stale lockfiles"
    find "$VMXDIR" -name \*.lck -type f -exec rm "{}" \;
    for BVM in `vmware_get_broken`; do
      ocf_log info "Unregistering missing virtual machine $BVM"
      vmware_unregister_vm $BVM 
    done
    if [ -z "$VMID" ]; then
      # VM is not registered, need to register
      ocf_log info "Virtual machine $VM is not registered"
      ocf_log info "Registering Virtual machine $VM"
      vmware_register_vm "$VMXPATH" 
      VMID=`vmware_get_vid "$RELVMXPATH"`
      if [ -z "$VMID" ]; then
        ocf_log err "Could not register virtual machine $VM: aborting."
        exit $OCF_ERR_GENERIC
      fi
      ocf_log info "Virtual machine $VM registered with ID $VMID"
    fi     
    ocf_log info "Powering on virtual machine $VM"
    vmware_poweron_vm $VMID 
    # Give the VM some time to initialize
    sleep 5
  
    if [ "$VMAUTOMSG" != "TRUE" ]; then 
      # msg.autoAnswer is not set: we try to deal with the
      # most common question: msg.uuid.altered 
      ocf_log info  "Checking msg.uuid.altered on VM $VM"
      if [ -n "`vmware_uuid_alt $VMID`" ]; then
        MSGID=`vmware_get_msgid $VMID`
        vmware_answer_msg $VMID $MSGID 2
      fi
    fi

    # Check if the VM is running. We don't bother 
    # with timeouts: we rely on the CRM for that.
    VMSTATE="`vmware_get_status $VMID`"
    while [ "$VMSTATE" != "Powered on" ]; do
      ocf_log info "Virtual machine $VM is still stopped: delaying 10 seconds"
      sleep 10
      VMSTATE="`vmware_get_status $VMID`"
    done
    
    ocf_log info "Virtual machine $VM is running"
    return $OCF_SUCCESS
  fi
}

# Stop a virtual machine
vmware_stop() {
  # Don't stop a VM if it's not registered
  if [ -z "$VMID" ]; then
    ocf_log info "Virtual machine $VM is not registered"
    return $OCF_SUCCESS
  else  
    # Don't stop a VM if it's already stopped
    if [ "$VMSTATE" != "Powered on" ]; then
      ocf_log info "Virtual machine $VM is already stopped"
    else
      # If the VM is running send a suspend signal and wait
      # until it is off. We don't bother with timeouts: we 
      # rely on the CRM for that.
      ocf_log info "Virtual machine $VM is running: suspending it"
      vmware_suspend_vm $VMID 
      sleep 5
      VMSTATE="`vmware_get_status $VMID`"
      while [ "$VMSTATE" = "Powered on" ]; do
        ocf_log info "Virtual machine $VM is still running: delaying 10 seconds"
        sleep 10
        VMSTATE="`vmware_get_status $VMID`"
      done
    fi
    # VMware randomly fails to unregister VMs,
    # so we send the command twice
    ocf_log info "Unregistering virtual machine $VM"
    vmware_unregister_vm $VMID 
    VMID=`vmware_get_vid "$RELVMXPATH"`
    if [ -n "$VMID" ]; then
      ocf_log warn "Could not unregister virtual machine $VM: retrying."
      sleep 5
      vmware_unregister_vm $VMID 
      VMID=`vmware_get_vid "$RELVMXPATH"`
      if [ -n "$VMID" ]; then
        ocf_log err "Could not unregister virtual machine $VM: aborting."   
        exit $OCF_ERR_GENERIC
      fi
    fi
    ocf_log info "Virtual machine $VM is stopped"
    return $OCF_SUCCESS
  fi
}

# Monitor a virtual machine
vmware_monitor() {
  if [ "$VMSTATE" = "Powered on" ]; then
    ocf_log info "Virtual machine $VM (ID $VMID) is running..."
    return $OCF_SUCCESS
  else
    ocf_log info "Virtual machine $VM is stopped/suspended/not registered"
    return $OCF_NOT_RUNNING
  fi
}

# Print metadata informations
meta_data() {
        cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="VMwareVM">
<version>0.2</version>
<longdesc lang="en">
OCF compliant script to control vmware server 2.0 virtual machines.
</longdesc>
<shortdesc lang="en">VMWare server 2.0 resource agent</shortdesc>

<parameters>
<parameter name="vmxpath" unique="0" required="1">
<longdesc lang="en">
VMX configuration file path
</longdesc>
<shortdesc lang="en">VMX file path</shortdesc>
<content type="string"/>
</parameter>

<parameter name="vimshbin" unique="0" required="1">
<longdesc lang="en">
vmware-vim-cmd executable path
</longdesc>
<shortdesc lang="en">vmware-vimsh path</shortdesc>
<content type="string" default="/usr/bin/vmware-vim-cmd"/>
</parameter>
</parameters>

<actions>
<action name="start"        timeout="900" />
<action name="stop"         timeout="900" />
<action name="monitor"      timeout="30" interval="300" depth="0" 
start-delay="60" />
<action name="meta-data"    timeout="5" />
</actions>
</resource-agent>
END
}

# See how we were called 
#################################################################

case $1 in
meta-data)      
  meta_data
  exit $OCF_SUCCESS
  ;;

start)
  vmware_validate
  vmware_start
  ;;

stop)
  vmware_validate
  vmware_stop
  ;;

status|monitor) 
  vmware_validate
  vmware_monitor
  ;;

usage|help)
  vmware_usage
  exit $OCF_SUCCESS
  ;;

validate-all)
  vmware_validate
  ;;

*)
  vmware_usage
  exit $OCF_ERR_UNIMPLEMENTED
  ;;

esac

exit $?


_______________________________________________
Pacemaker mailing list
Pacemaker@oss.clusterlabs.org
http://oss.clusterlabs.org/mailman/listinfo/pacemaker

Reply via email to