diff options
Diffstat (limited to '')
-rw-r--r-- | agents/vmware/fence_vmware.py | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/agents/vmware/fence_vmware.py b/agents/vmware/fence_vmware.py new file mode 100644 index 0000000..bc1785f --- /dev/null +++ b/agents/vmware/fence_vmware.py @@ -0,0 +1,336 @@ +#!@PYTHON@ -tt + +# +# The Following agent has been tested on: +# vmrun 2.0.0 build-116503 (from VMware Server 2.0) against: +# VMware ESX 4.0.0 +# VMware vCenter 4.0.0 +# VMware ESX 3.5 +# VMware Server 2.0.0 +# VMware ESXi 3.5 update 2 +# VMware Server 1.0.7 (works but list/status show only running VMs) +# +# VI Perl API 1.6 against: +# VMware ESX 4.0.0 +# VMware vCenter 4.0.0 +# VMware ESX 3.5 +# VMware ESXi 3.5 update 2 +# VMware Virtual Center 2.5 +# +# VMware vSphere SDK for Perl 4.0.0 against: +# VMware ESX 4.0.0 +# VMware vCenter 4.0.0 +# + +import sys, re, pexpect +import logging +import atexit +sys.path.append("@FENCEAGENTSLIBDIR@") +from fencing import * +from fencing import fail, fail_usage, EC_TIMED_OUT, run_delay, frun + +### 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_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 + +# Quote string for proper existence in quoted string used for pexpect.run function +# Ex. test'this will return test'\''this. So pexpect run will really pass ' to argument +def quote_for_run(text): + dstr = '' + + for c in text: + if c == r"'": + dstr += "'\\''" + else: + dstr += c + + return dstr + +# Return string with command and additional parameters (something like vmrun -h 'host' +def vmware_prepare_command(options, add_login_params, additional_params): + res = options["--exec"] + + if add_login_params: + if vmware_internal_type == VMWARE_TYPE_ESX: + res += " --server '%s' --username '%s' --password '%s' "% (quote_for_run(options["--ip"]), + quote_for_run(options["--username"]), + quote_for_run(options["--password"])) + elif vmware_internal_type == VMWARE_TYPE_SERVER2: + res += " -h 'https://%s/sdk' -u '%s' -p '%s' -T server "% (quote_for_run(options["--ip"]), + quote_for_run(options["--username"]), + quote_for_run(options["--password"])) + elif vmware_internal_type == VMWARE_TYPE_SERVER1: + host_name_array = options["--ip"].split(':') + + res += " -h '%s' -u '%s' -p '%s' -T server1 "% (quote_for_run(host_name_array[0]), + quote_for_run(options["--username"]), + quote_for_run(options["--password"])) + if len(host_name_array) > 1: + res += "-P '%s' "% (quote_for_run(host_name_array[1])) + + if "--vmware-datacenter" in options and vmware_internal_type == VMWARE_TYPE_ESX: + res += "--datacenter '%s' "% (quote_for_run(options["--vmware-datacenter"])) + + if additional_params != "": + res += additional_params + + return res + +# 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: + logging.debug("%s\n", command) + + (res_output, res_code) = frun(command, + int(options["--shell-timeout"]) + int(options["--login-timeout"]) + additional_timeout, True) + + if res_code == None: + fail(EC_TIMED_OUT) + if res_code != 0 and add_login_params: + logging.debug("%s\n", res_output) + fail_usage("%s returned %s"% (options["--exec"], res_output)) + else: + logging.debug("%s\n", res_output) + + except pexpect.ExceptionPexpect: + fail_usage("Cannot run command %s"% (options["--exec"])) + + 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(options, add_vm_name): + outlets = {} + + if add_vm_name: + all_machines = vmware_run_command(options, True, + ("--operation status --vmname '%s'"% (quote_for_run(options["--plug"]))), 0) + else: + all_machines = vmware_run_command(options, True, "--operation list", int(options["--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(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): + del conn + + if vmware_internal_type == VMWARE_TYPE_ESX: + return vmware_get_outlets_vi(options, False) + if vmware_internal_type == VMWARE_TYPE_SERVER1 or vmware_internal_type == VMWARE_TYPE_SERVER2: + return vmware_get_outlets_vix(options) + +def get_power_status(conn, options): + if vmware_internal_type == VMWARE_TYPE_ESX: + outlets = vmware_get_outlets_vi(options, True) + else: + outlets = get_outlets_status(conn, options) + + if vmware_internal_type == VMWARE_TYPE_SERVER2 or vmware_internal_type == VMWARE_TYPE_ESX: + if not options["--plug"] in outlets: + fail_usage("Failed: You have to enter existing name of virtual machine!") + else: + return outlets[options["--plug"]][1] + elif vmware_internal_type == VMWARE_TYPE_SERVER1: + return (options["--plug"] in outlets) and "on" or "off" + +def set_power_status(conn, options): + del conn + + if vmware_internal_type == VMWARE_TYPE_ESX: + additional_params = "--operation %s --vmname '%s'" % \ + ((options["--action"] == "on" and "on" or "off"), quote_for_run(options["--plug"])) + elif vmware_internal_type == VMWARE_TYPE_SERVER1 or vmware_internal_type == VMWARE_TYPE_SERVER2: + additional_params = "%s '%s'" % \ + ((options["--action"] == "on" and "start" or "stop"), quote_for_run(options["--plug"])) + if options["--action"] == "off": + additional_params += " hard" + + vmware_run_command(options, True, additional_params, int(options["--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(r"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 + +# Check vmware type, set vmware_internal_type to one of VMWARE_TYPE_ value and +# options["--exec"] to path (if not specified) +def vmware_check_vmware_type(options): + global vmware_internal_type + + options["--vmware_type"] = options["--vmware_type"].lower() + + if options["--vmware_type"] == "esx": + vmware_internal_type = VMWARE_TYPE_ESX + if "--exec" not in options: + options["--exec"] = VMHELPER_COMMAND + elif options["--vmware_type"] == "server2": + vmware_internal_type = VMWARE_TYPE_SERVER2 + if "--exec" not in options: + options["--exec"] = VMRUN_COMMAND + elif options["--vmware_type"] == "server1": + vmware_internal_type = VMWARE_TYPE_SERVER1 + if "--exec" not in options: + options["--exec"] = VMRUN_COMMAND + else: + fail_usage("vmware_type can be esx,server2 or server1!") + +# Main agent method +def main(): + device_opt = ["ipaddr", "login", "passwd", "secure", + "exec", "vmware_type", "vmware_datacenter"] + + atexit.register(atexit_handler) + + all_opt["secure"]["default"] = "1" + all_opt["vmware_type"]["default"] = VMWARE_DEFAULT_TYPE + + options = check_input(device_opt, process_input(device_opt)) + + docs = {} + docs["shortdesc"] = "Fence agent for VMWare" + docs["longdesc"] = "fence_vmware is an I/O Fencing agent \ +which can be used with the VMware ESX, VMware ESXi or VMware Server \ +to fence virtual machines.\ +\n.P\n\ +Before you can use this agent, it must be installed VI Perl Toolkit or \ +vmrun command on every node you want to make fencing.\ +\n.P\n\ +VI Perl Toolkit is preferred for VMware ESX/ESXi and Virtual Center. Vmrun \ +command is only solution for VMware Server 1/2 (this command will works against \ +ESX/ESXi 3.5 up2 and VC up2 too, but not cluster aware!) and is available as part \ +of VMware VIX API SDK package. VI Perl and VIX API SDK are both available from \ +VMware web pages (not int RHEL repository!). \ +\n.P\n\ +You can specify type of VMware you are connecting to with \\fB-d\\fP switch \ +(or \\fIvmware_type\\fR for stdin). Possible values are esx, server2 and server1.\ +Default value is esx, which will use VI Perl. With server1 and server2, vmrun \ +command is used.\ +\n.P\n\ +After you have successfully installed VI Perl Toolkit or VIX API, you should \ +be able to run fence_vmware_helper (part of this agent) or vmrun command. \ +This agent supports only vmrun from version 2.0.0 (VIX API 1.6.0)." + docs["vendorurl"] = "http://www.vmware.com" + show_docs(options, docs) + + run_delay(options) + + # 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 + result = fence_action(None, options, set_power_status, get_power_status, get_outlets_status) + + sys.exit(result) + +if __name__ == "__main__": + main() |