Skip to content

Instantly share code, notes, and snippets.

@jbarber
Created June 21, 2011 09:57
Show Gist options
  • Save jbarber/1037554 to your computer and use it in GitHub Desktop.
Save jbarber/1037554 to your computer and use it in GitHub Desktop.
RHEL5 RHCS fence agent for VMware
#!/usr/bin/python
#
# The Following agent has been tested on:
# vmrun 2.0.0 build-116503 (from VMware Server 2.0) against:
# VMware ESX 3.5 (works correctly)
# VMware Server 2.0.0 (works correctly)
# VMware ESXi 3.5 update 2 (works correctly)
# VMware Server 1.0.7 (works but list/status show only running VMs)
#
# VI Perl API 1.6 against:
# VMware ESX 3.5
# VMware ESXi 3.5 update 2
# VMware Virtual Center 2.5
#
import sys, re, pexpect
sys.path.append("/usr/lib/fence")
from fencing import *
#BEGIN_VERSION_GENERATION
FENCE_RELEASE_NAME="DEVEL.1231929034";
REDHAT_COPYRIGHT=("Copyright (C) Red Hat, Inc. 2004 All rights reserved.")
BUILD_DATE="(built Wed Jan 14 11:46:56 CET 2009)";
#END_VERSION_GENERATION
### CONSTANTS ####
# VMware type is ESX/ESXi/VC
VMWARE_TYPE_ESX=0
# VMware type is Server 1.x
VMWARE_TYPE_SERVER1=1
# VMware type is Server 2.x and/or ESX 3.5 up2, ESXi 3.5 up2, VC 2.5 up2
VMWARE_TYPE_SERVER2=2
# Minimum required version of vmrun command
VMRUN_MINIMUM_REQUIRED_VERSION=2
# Default path to vmhelper command
VMHELPER_COMMAND="fence_vmware_ng_helper"
# Default path to vmrun command
VMRUN_COMMAND="/usr/bin/vmrun"
# Default type of vmware
VMWARE_DEFAULT_TYPE="esx"
#### GLOBAL VARIABLES ####
# Internal type. One of VMWARE_TYPE_, set by #vmware_check_vmware_type
vmware_internal_type=VMWARE_TYPE_ESX
# If ESX is disconnected, say, that VM is off (don't return previous state)
vmware_disconnected_hack=False
### FUNCTIONS ####
#Split string in simplified DSV format to array of items
def dsv_split(dsv_str):
delimiter_c=':'
escape_c='\\'
res=[]
status=0
tmp_str=""
for x in dsv_str:
if (status==0):
if (x==delimiter_c):
res.append(tmp_str)
tmp_str=""
elif (x==escape_c):
status=1
else:
tmp_str+=x
elif (status==1):
if (x==delimiter_c):
tmp_str+=delimiter_c
elif (x==escape_c):
tmp_str+=escape_c
else:
tmp_str+=escape_c+x
status=0
if (tmp_str!=""):
res.append(tmp_str)
return res
# Return string with command and additional parameters (something like vmrun -h 'host'
def vmware_prepare_command(options,add_login_params,additional_params):
res=options["-e"]
if (add_login_params):
if (vmware_internal_type==VMWARE_TYPE_ESX):
res+=" --server '%s' --username '%s' --password '%s' "%(options["-a"],options["-l"],options["-p"])
elif (vmware_internal_type==VMWARE_TYPE_SERVER2):
res+=" -h 'https://%s/sdk' -u '%s' -p '%s' -T server "%(options["-a"],options["-l"],options["-p"])
elif (vmware_internal_type==VMWARE_TYPE_SERVER1):
host_name_array=options["-a"].split(':')
res+=" -h '%s' -u '%s' -p '%s' -T server1 "%(host_name_array[0],options["-l"],options["-p"])
if (len(host_name_array)>1):
res+="-P '%s' "%(host_name_array[1])
if ((options.has_key("-s")) and (vmware_internal_type==VMWARE_TYPE_ESX)):
res+="--datacenter '%s' "%(options["-s"])
if (additional_params!=""):
res+=additional_params
return res
# Log message if user set verbose option
def vmware_log(options, message):
if options["log"] >= LOG_MODE_VERBOSE:
options["debug_fh"].write(message+"\n")
# Run command with timeout and parameters. Internaly uses vmware_prepare_command. Returns string
# with output from vmrun command. If something fails (command not found, exit code is not 0), fail_usage
# function is called (and never return).
def vmware_run_command(options,add_login_params,additional_params,additional_timeout):
command=vmware_prepare_command(options,add_login_params,additional_params)
try:
vmware_log(options,command)
(res_output,res_code)=pexpect.run(command,SHELL_TIMEOUT+LOGIN_TIMEOUT+additional_timeout,True)
if (res_code==None):
fail(EC_TIMED_OUT)
if ((res_code!=0) and (add_login_params)):
vmware_log(options,res_output)
fail_usage("%s returned %s"%(options["-e"],res_output))
else:
vmware_log(options,res_output)
except pexpect.ExceptionPexpect:
fail_usage("Cannot run command %s"%(options["-e"]))
return res_output
# Get outlet list with status as hash table. If you will use add_vm_name, only VM with vmname is
# returned. This is used in get_status function
def vmware_get_outlets_vi(conn, options, add_vm_name):
outlets={}
if (add_vm_name):
all_machines=vmware_run_command(options,True,("--operation status --vmname '%s'"%(options["-n"])),0)
else:
all_machines=vmware_run_command(options,True,"--operation list",POWER_TIMEOUT)
all_machines_array=all_machines.splitlines()
for machine in all_machines_array:
machine_array=dsv_split(machine)
if (len(machine_array)==4):
if (machine_array[0] in outlets):
fail_usage("Failed. More machines with same name %s found!"%(machine_array[0]))
if (vmware_disconnected_hack):
outlets[machine_array[0]]=("",(
((machine_array[2].lower() in ["poweredon"]) and
(machine_array[3].lower()=="connected"))
and "on" or "off"))
else:
outlets[machine_array[0]]=("",((machine_array[2].lower() in ["poweredon"]) and "on" or "off"))
return outlets
# Get outlet list with status as hash table.
def vmware_get_outlets_vix(conn,options):
outlets={}
running_machines=vmware_run_command(options,True,"list",0)
running_machines_array=running_machines.splitlines()[1:]
if (vmware_internal_type==VMWARE_TYPE_SERVER2):
all_machines=vmware_run_command(options,True,"listRegisteredVM",0)
all_machines_array=all_machines.splitlines()[1:]
elif (vmware_internal_type==VMWARE_TYPE_SERVER1):
all_machines_array=running_machines_array
for machine in all_machines_array:
if (machine!=""):
outlets[machine]=("",((machine in running_machines_array) and "on" or "off"))
return outlets
def get_outlets_status(conn, options):
if (vmware_internal_type==VMWARE_TYPE_ESX):
return vmware_get_outlets_vi(conn,options,False)
if ((vmware_internal_type==VMWARE_TYPE_SERVER1) or (vmware_internal_type==VMWARE_TYPE_SERVER2)):
return vmware_get_outlets_vix(conn,options)
def get_power_status(conn,options):
if (vmware_internal_type==VMWARE_TYPE_ESX):
outlets=vmware_get_outlets_vi(conn,options,True)
else:
outlets=get_outlets_status(conn,options,False)
if ((vmware_internal_type==VMWARE_TYPE_SERVER2) or (vmware_internal_type==VMWARE_TYPE_ESX)):
if (not (options["-n"] in outlets)):
fail_usage("Failed: You have to enter existing name of virtual machine!")
else:
return outlets[options["-n"]][1]
elif (vmware_internal_type==VMWARE_TYPE_SERVER1):
return ((options["-n"] in outlets) and "on" or "off")
def set_power_status(conn, options):
if (vmware_internal_type==VMWARE_TYPE_ESX):
additional_params="--operation %s --vmname '%s'"%((options["-o"]=="on" and "on" or "off"),options["-n"])
elif ((vmware_internal_type==VMWARE_TYPE_SERVER1) or (vmware_internal_type==VMWARE_TYPE_SERVER2)):
additional_params="%s '%s'"%((options["-o"]=="on" and "start" or "stop"),options["-n"])
if (options["-o"]=="off"):
additional_params+=" hard"
vmware_run_command(options,True,additional_params,POWER_TIMEOUT)
# Returns True, if user uses supported vmrun version (currently >=2.0.0) otherwise False.
def vmware_is_supported_vmrun_version(options):
vmware_help_str=vmware_run_command(options,False,"",0)
version_re=re.search("vmrun version (\d\.(\d[\.]*)*)",vmware_help_str.lower())
if (version_re==None):
return False # Looks like this "vmrun" is not real vmrun
version_array=version_re.group(1).split(".")
try:
if (int(version_array[0])<VMRUN_MINIMUM_REQUIRED_VERSION):
return False
except Exception:
return False
return True
# Define new options
def vmware_define_defaults():
all_opt["vmware_type"]["default"]=VMWARE_DEFAULT_TYPE
# Check vmware type, set vmware_internal_type to one of VMWARE_TYPE_ value and
# options["-e"] to path (if not specified)
def vmware_check_vmware_type(options):
global vmware_internal_type
if (not options.has_key("-d")):
options["-d"]="esx"
else:
options["-d"]=options["-d"].lower()
if (options["-d"]=="esx"):
vmware_internal_type=VMWARE_TYPE_ESX
if (not options.has_key("-e")):
options["-e"]=VMHELPER_COMMAND
elif (options["-d"]=="server2"):
vmware_internal_type=VMWARE_TYPE_SERVER2
if (not options.has_key("-e")):
options["-e"]=VMRUN_COMMAND
elif (options["-d"]=="server1"):
vmware_internal_type=VMWARE_TYPE_SERVER1
if (not options.has_key("-e")):
options["-e"]=VMRUN_COMMAND
else:
fail_usage("vmware_type can be esx,server2 or server1!")
# Define new options
def vmware_define_new_options():
all_opt["exec"]={
"getopt" : "e:",
"help" : "-e Command to execute",
"order" : 1 };
all_opt["vmware_type"]={
"getopt" : "d:",
"help" : "-d Type of VMware to connect",
"order" : 1 };
all_opt["vmware_datacenter"]={
"getopt" : "s:",
"help" : "-s VMWare datacenter filter",
"order" : 2 };
# Main agent method
def main():
device_opt = [ "help", "version", "agent", "quiet", "verbose", "debug",
"action", "ipaddr", "login", "passwd", "passwd_script",
"test", "port", "exec", "vmware_type",
"vmware_datacenter", "secure" ]
vmware_define_new_options()
vmware_define_defaults()
options = check_input(device_opt, process_input(device_opt))
# Default is secure connection
options["-x"] = 1
# Check vmware type and set path
vmware_check_vmware_type(options)
# Test user vmrun command version
if ((vmware_internal_type==VMWARE_TYPE_SERVER1) or (vmware_internal_type==VMWARE_TYPE_SERVER2)):
if (not (vmware_is_supported_vmrun_version(options))):
fail_usage("Unsupported version of vmrun command! You must use at least version %d!"%(VMRUN_MINIMUM_REQUIRED_VERSION))
# Operate the fencing device
fence_action(None, options, set_power_status, get_power_status)
if __name__ == "__main__":
main()
#!/usr/bin/perl
use strict;
use warnings;
my ($FENCE_RELEASE_NAME, $REDHAT_COPYRIGHT, $BUILD_DATE);
#BEGIN_VERSION_GENERATION
$FENCE_RELEASE_NAME="DEVEL.1231929034";
$REDHAT_COPYRIGHT=("Copyright (C) Red Hat, Inc. 2004 All rights reserved.");
$BUILD_DATE="(built Wed Jan 14 11:46:56 CET 2009)";
#END_VERSION_GENERATION
#### FUNCTIONS #####
# Show error message
sub show_error {
print STDERR @_;
}
sub my_exit {
my ($exit_code)=@_;
# Disconnect from server
Util::disconnect();
exit $exit_code;
}
# Convert one field (string) to format acceptable by DSV. This
# means replace any : with \: and \ with \\.
sub convert_field_to_dsv {
my ($input_line)=@_;
$input_line =~ s/([\\:])/\\$1/g;
return $input_line
}
#### Global variables #####
# Aditional options
my %opts = (
'operation' => {
type => "=s",
help => "The operation to perform (on,off,list,status). "
. "Operations on/off/status require name of the virtual machine",
default => "list",
required => 0,
},
'vmname' => {
type => "=s",
help => "The name of the virtual machine",
required => 0,
},
'datacenter' => {
type => "=s",
help => "The name of the datacenter",
required => 0,
}
);
#################
##### MAIN ######
#################
# Conditional use of VIRuntime
eval "use VMware::VIRuntime;";
if ($@) {
show_error "Please install VI Perl API package to use this tool!\n";
exit 1;
}
# Parse options
Opts::add_options(%opts);
Opts::parse();
Opts::validate();
if (!(Opts::get_option('operation')=~/^(on|off|list|status)$/i)) {
show_error "Operation should be on, off, list or status!\n";
exit 2;
}
my $operation=lc(Opts::get_option('operation'));
if (($operation ne 'list') && (!defined Opts::get_option('vmname'))) {
show_error "Operation on, off, status require vmname parameter!\n";
exit 2;
}
# Try connect to machine
eval {
Util::connect();
};
if ($@) {
show_error "Cannot connect to server!\nVMware error:".$@;
exit 3;
}
my ($datacenter, $datacenter_view, $vm_views,$vm);
# We are connected to machine
# If user want's datacenter, we must first find datacenter
my %filter=(view_type => 'VirtualMachine');
if( defined (Opts::get_option('datacenter')) ) {
$datacenter = Opts::get_option('datacenter');
$datacenter_view = Vim::find_entity_view(view_type => 'Datacenter',
filter => { name => $datacenter });
if (!$datacenter_view) {
show_error "Cannot find datacenter ".$datacenter."!\n";
my_exit 4;
}
$filter{'begin_entity'}=$datacenter_view;
}
if ($operation ne 'list') {
$filter{'filter'}= {"config.name" => Opts::get_option('vmname')};
}
$vm_views = Vim::find_entity_views(%filter);
my $found=0;
# Traverse all found vm
foreach $vm(@$vm_views) {
if (($operation eq 'list') or ($operation eq 'status')) {
if (!$vm->summary->config->template) {
print convert_field_to_dsv($vm->name).":".
convert_field_to_dsv($vm->summary->config->vmPathName).":".
convert_field_to_dsv($vm->runtime->powerState->val).":".
convert_field_to_dsv($vm->runtime->connectionState->val)."\n";
}
} elsif ($operation eq 'on') {
eval {
$vm->PowerOnVM();
};
if ($@) {
# If error is SoapFault with InvalidPowerState, user maybe use some auto power on tool.
# This is not error, warning is enought.
if (ref($@) eq 'SoapFault') {
if (ref($@->detail) eq 'InvalidPowerState') {
show_error "Warning: Cannot power on vm (somebody done it before???) ".Opts::get_option('vmname').
"!\nVMware error:".$@."\n";
}
} else {
# Some other more serious problem
show_error "Cannot power on vm ".Opts::get_option('vmname')."!\nVMware error:".$@."\n";
my_exit 6;
}
}
} elsif ($operation eq 'off') {
eval {
$vm->PowerOffVM();
};
if ($@) {
# If error is SoapFault with InvalidPowerState, user maybe use some auto power off tool.
# This is not error, warning is enought.
if (ref($@) eq 'SoapFault') {
if (ref($@->detail) eq 'InvalidPowerState') {
show_error "Warning: Cannot power off vm (somebody done it before???) ".Opts::get_option('vmname').
"!\nVMware error:".$@."\n";
}
} else {
# Some other more serious problem
show_error "Cannot power off vm ".Opts::get_option('vmname')."!\nVMware error:".$@."\n";
my_exit 6;
}
}
} else {
show_error "Operation should be on, off or list!\n";
my_exit 2;
}
$found++;
}
if ((!$found) && ($operation ne 'list')) {
show_error "Cannot find vm ".Opts::get_option('vmname')."!\n";
my_exit 5;
}
# Should be 0 -> success all, or 6 in case of error
my_exit 0;
__END__
=head1 NAME
fence_vmware_helper - Perform list of virtual machines and
poweron, poweroff of operations on virtual machines.
=head1 SYNOPSIS
fence_vmware_helper --operation <on|off|list|status> [options]
=head1 DESCRIPTION
This VI Perl command-line utility provides an interface for
seven common provisioning operations on one or more virtual
machines: powering on, powering off and listing virtual mode.
=head1 OPTIONS
=head2 GENERAL OPTIONS
=over
=item B<operation>
Operation to be performed. One of the following:
<on> (power on one or more virtual machines),
<off> (power off one or more virtual machines),
<list> (list virtual machines and their status)
<status> (same as list, but show only machines with vmname)
=item B<vmname>
Optional. Name of the virtual machine on which the
operation is to be performed.
=item B<datacenter>
Optional. Name of the datacenter for the virtual machine(s).
Operations will be performed on all the virtual machines under the given datacenter.
=back
=head1 EXAMPLES
Power on a virtual machine
fence_vmware_helper --username administrator --password administrator --operation on
--vmname rhel --server win1
fence_vmware_helper --username administrator --password administrator --operation on
--vmname rhel --server win1 --datacenter Datacenter
Power off a virtual machine
fence_vmware_helper --username administrator --password administrator --operation off
--vmname rhel --server win1
perl fence_vmware_helper --username administrator --password administrator --operation off
--vmname rhel --server win1 --datacenter Datacenter
List of virtual machines
fence_vmware_helper --username administrator --password administrator --server win1
fence_vmware_helper --username administrator --password administrator --server win1
--operation list
Get status of virtual machine
fence_vmware_helper --username administrator --password administrator --server win1
--vmname rhel --operation status
=head1 SUPPORTED PLATFORMS
All operations supported on ESX 3.0.1
All operations supported on Virtual Center 2.0.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment