diff --git a/fence/agents/apc_snmp/fence_apc_snmp.py b/fence/agents/apc_snmp/fence_apc_snmp.py index 29aafe62..d91e9e95 100644 --- a/fence/agents/apc_snmp/fence_apc_snmp.py +++ b/fence/agents/apc_snmp/fence_apc_snmp.py @@ -1,208 +1,208 @@ #!/usr/bin/python -tt # The Following agent has been tested on: # - APC Switched Rack PDU - SNMP v1 # (MB:v3.7.0 PF:v2.7.0 PN:apc_hw02_aos_270.bin AF1:v2.7.3 # AN1:apc_hw02_aos_270.bin AF1:v2.7.3 AN1:apc_hw02_rpdu_273.bin MN:AP7930 HR:B2) # - APC Web/SNMP Management Card - SNMP v1 and v3 (noAuthNoPrivacy,authNoPrivacy, authPrivacy) # (MB:v3.8.6 PF:v3.5.8 PN:apc_hw02_aos_358.bin AF1:v3.5.7 # AN1:apc_hw02_aos_358.bin AF1:v3.5.7 AN1:apc_hw02_rpdu_357.bin MN:AP7900 HR:B2) # - APC Switched Rack PDU - SNMP v1 # (MB:v3.7.0 PF:v2.7.0 PN:apc_hw02_aos_270.bin AF1:v2.7.3 # AN1:apc_hw02_rpdu_273.bin MN:AP7951 HR:B2) # - Tripplite PDUMH20HVNET 12.04.0055 - SNMP v1, v2c, v3 import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing_snmp import FencingSnmp #BEGIN_VERSION_GENERATION RELEASE_VERSION="APC SNMP fence agent" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION ### CONSTANTS ### # oid defining fence device OID_SYS_OBJECT_ID = '.1.3.6.1.2.1.1.2.0' ### GLOBAL VARIABLES ### # Device - see ApcRPDU, ApcMSP, ApcMS, TripplitePDU device = None # Port ID port_id = None # Switch ID switch_id = None # Classes describing Device params class TripplitePDU: # Rack PDU status_oid = '.1.3.6.1.4.1.850.10.2.3.5.1.2.1.%d' control_oid = '.1.3.6.1.4.1.850.10.2.3.5.1.4.1.%d' outlet_table_oid = '.1.3.6.1.4.1.850.10.2.3.5.1.5' ident_str = "Tripplite" state_on = 2 state_off = 1 turn_on = 2 turn_off = 1 has_switches = False class ApcRPDU: # Rack PDU status_oid = '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.%d' control_oid = '.1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%d' outlet_table_oid = '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.2' ident_str = "APC rPDU" state_on = 1 state_off = 2 turn_on = 1 turn_off = 2 has_switches = False class ApcMSP: # Master Switch+ status_oid = '.1.3.6.1.4.1.318.1.1.6.7.1.1.5.%d.1.%d' control_oid = '.1.3.6.1.4.1.318.1.1.6.5.1.1.5.%d.1.%d' outlet_table_oid = '.1.3.6.1.4.1.318.1.1.6.7.1.1.4' ident_str = "APC Master Switch+" state_on = 1 state_off = 2 turn_on = 1 turn_off = 3 has_switches = True class ApcMS: # Master Switch - seems oldest, but supported on every APC PDU status_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d' control_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d' outlet_table_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.4' ident_str = "APC Master Switch (fallback)" state_on = 1 state_off = 2 turn_on = 1 turn_off = 2 has_switches = False ### FUNCTIONS ### -def apc_set_device(conn, options): +def apc_set_device(conn): global device agents_dir = {'.1.3.6.1.4.1.318.1.3.4.5':ApcRPDU, '.1.3.6.1.4.1.318.1.3.4.4':ApcMSP, '.1.3.6.1.4.1.850.1':TripplitePDU, None:ApcMS} # First resolve type of APC apc_type = conn.walk(OID_SYS_OBJECT_ID) if (not ((len(apc_type)==1) and (agents_dir.has_key(apc_type[0][1])))): apc_type = [[None, None]] device = agents_dir[apc_type[0][1]] conn.log_command("Trying %s"%(device.ident_str)) def apc_resolv_port_id(conn, options): global port_id, switch_id if (device == None): - apc_set_device(conn, options) + apc_set_device(conn) # Now we resolv port_id/switch_id if ((options["--plug"].isdigit()) and ((not device.has_switches) or (options["--switch"].isdigit()))): port_id = int(options["--plug"]) if (device.has_switches): switch_id = int(options["--switch"]) else: table = conn.walk(device.outlet_table_oid, 30) for x in table: if (x[1].strip('"') == options["--plug"]): t = x[0].split('.') if (device.has_switches): port_id = int(t[len(t)-1]) switch_id = int(t[len(t)-3]) else: port_id = int(t[len(t)-1]) if (port_id == None): fail_usage("Can't find port with name %s!"%(options["--plug"])) def get_power_status(conn, options): if (port_id == None): apc_resolv_port_id(conn, options) oid = ((device.has_switches) and device.status_oid%(switch_id, port_id) or device.status_oid%(port_id)) (oid, status) = conn.get(oid) return (status==str(device.state_on) and "on" or "off") def set_power_status(conn, options): if (port_id == None): apc_resolv_port_id(conn, options) oid = ((device.has_switches) and device.control_oid%(switch_id, port_id) or device.control_oid%(port_id)) conn.set(oid, (options["--action"]=="on" and device.turn_on or device.turn_off)) def get_outlets_status(conn, options): result = {} if (device == None): - apc_set_device(conn, options) + apc_set_device(conn) res_ports = conn.walk(device.outlet_table_oid, 30) for x in res_ports: t = x[0].split('.') port_num = ((device.has_switches) and "%s:%s"%(t[len(t)-3], t[len(t)-1]) or "%s"%(t[len(t)-1])) port_name = x[1].strip('"') port_status = "" result[port_num] = (port_name, port_status) return result # Main agent method def main(): device_opt = [ "ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "community" ] atexit.register(atexit_handler) all_opt["snmp_version"]["default"] = "1" all_opt["community"]["default"] = "private" options = check_input(device_opt, process_input(device_opt)) ## Support for -n [switch]:[plug] notation that was used before if ((options.has_key("--plug")) and (-1 != options["--plug"].find(":"))): (switch, plug) = options["--plug"].split(":", 1) if ((switch.isdigit()) and (plug.isdigit())): options["--switch"] = switch options["--plug"] = plug if (not (options.has_key("--switch"))): options["--switch"] = "1" docs = { } docs["shortdesc"] = "Fence agent for APC, Tripplite PDU over SNMP" docs["longdesc"] = "fence_apc_snmp is an I/O Fencing agent \ which can be used with the APC network power switch or Tripplite PDU devices.\ It logs into a device via SNMP and reboots a specified outlet. It supports \ SNMP v1, v2c, v3 with all combinations of authenticity/privacy settings." docs["vendorurl"] = "http://www.apc.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/cisco_ucs/fence_cisco_ucs.py b/fence/agents/cisco_ucs/fence_cisco_ucs.py index d2777def..a9ca8fe6 100644 --- a/fence/agents/cisco_ucs/fence_cisco_ucs.py +++ b/fence/agents/cisco_ucs/fence_cisco_ucs.py @@ -1,160 +1,165 @@ #!/usr/bin/python -tt import sys, re import pycurl, StringIO import logging import time import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_STATUS, EC_LOGIN_DENIED, LOG_MODE_VERBOSE #BEGIN_VERSION_GENERATION RELEASE_VERSION="New Cisco UCS Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="March, 2008" #END_VERSION_GENERATION RE_COOKIE = re.compile("", int(options["--shell-timeout"])) result = RE_STATUS.search(res) if (result == None): fail(EC_STATUS) else: status = result.group(1) if (status == "up"): return "on" else: return "off" def set_power_status(conn, options): + del conn + action = { 'on' : "up", 'off' : "down" }[options["--action"]] send_command(options, "" + "" + "" + "", int(options["--shell-timeout"])) return def get_list(conn, options): + del conn outlets = { } try: res = send_command(options, "", int(options["--shell-timeout"])) lines = res.split("", int(options["--login-timeout"])) result = RE_COOKIE.search(res) if (result == None): ## Cookie is absenting in response fail(EC_LOGIN_DENIED) except: fail(EC_LOGIN_DENIED) options["cookie"] = result.group(1) ## ## Modify suborg to format /suborg if options["--suborg"] != "": options["--suborg"] = "/" + options["--suborg"].lstrip("/").rstrip("/") ## ## Fence operations #### result = fence_action(None, options, set_power_status, get_power_status, get_list) ### Logout; we do not care about result as we will end in any case send_command(options, "", int(options["--shell-timeout"])) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/dummy/fence_dummy.py b/fence/agents/dummy/fence_dummy.py index ff67e7b1..aa1e5c23 100644 --- a/fence/agents/dummy/fence_dummy.py +++ b/fence/agents/dummy/fence_dummy.py @@ -1,135 +1,142 @@ #!/usr/bin/python -tt import sys, random import logging import time import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage #BEGIN_VERSION_GENERATION RELEASE_VERSION="New Dummy Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION plug_status = "on" def get_power_status_file(conn, options): + del conn + try: status_file = open(options["--status-file"], 'r') except: return "off" status = status_file.read() status_file.close() return status.lower() def set_power_status_file(conn, options): + del conn + if not (options["--action"] in [ "on", "off" ]): return status_file = open(options["--status-file"], 'w') status_file.write(options["--action"]) status_file.close() def get_power_status_fail(conn, options): outlets = get_outlets_fail(conn, options) if len(outlets) == 0 or options.has_key("--plug") == 0: fail_usage("Failed: You have to enter existing machine!") else: return outlets[options["--plug"]][0] def set_power_status_fail(conn, options): global plug_status + del conn plug_status = "unknown" if options["--action"] == "on": plug_status = "off" def get_outlets_fail(conn, options): + del conn + result = {} global plug_status if options["--action"] == "on": plug_status = "off" # This fake agent has no port data to list, so we have to make # something up for the list action. if options.has_key("--action") and options["--action"] == "list": result["fake_port_1"] = [plug_status, "fake"] result["fake_port_2"] = [plug_status, "fake"] elif (options.has_key("--plug") == 0): fail_usage("Failed: You have to enter existing machine!") else: port = options["--plug"] result[port] = [plug_status, "fake"] return result def main(): device_opt = [ "no_password", "status_file", "random_sleep_range", "type", "port" ] atexit.register(atexit_handler) all_opt["status_file"] = { "getopt" : "s:", "longopt" : "status-file", "help":"--status-file=[file] Name of file that holds current status", "required" : "0", "shortdesc" : "File with status", "default" : "/tmp/fence_dummy.status", "order": 1 } all_opt["random_sleep_range"] = { "getopt" : "r:", "longopt" : "random_sleep_range", "help":"--random_sleep_range=[seconds] Issue a sleep between 1 and [seconds]", "required" : "0", "shortdesc" : "Issue a sleep between 1 and X seconds. Used for testing.", "order": 1 } all_opt["type"] = { "getopt" : "t:", "longopt" : "type", "help":"--type=[type] Possible types are: file and fail", "required" : "0", "shortdesc" : "Type of the dummy fence agent", "default" : "file", "order": 1 } pinput = process_input(device_opt) if (pinput.has_key("--type") and pinput["--type"] == "file") or (pinput.has_key("--type") == False): # hack to have fence agents that require ports 'fail' and one that do not 'file' device_opt.remove("port") options = check_input(device_opt, process_input(device_opt)) docs = { } docs["shortdesc"] = "Dummy fence agent" docs["longdesc"] = "fence_dummy" docs["vendorurl"] = "http://www.example.com" show_docs(options, docs) # random sleep for testing if options.has_key("--random_sleep_range"): val = int(options["--random_sleep_range"]) ran = random.randint(1, val) logging.info("Random sleep for %d seconds\n" % ran) time.sleep(ran) if options["--type"] == "fail": result = fence_action(None, options, set_power_status_fail, get_power_status_fail, get_outlets_fail) else: result = fence_action(None, options, set_power_status_file, get_power_status_file, None) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/eaton_snmp/fence_eaton_snmp.py b/fence/agents/eaton_snmp/fence_eaton_snmp.py index 970fd690..9688516d 100644 --- a/fence/agents/eaton_snmp/fence_eaton_snmp.py +++ b/fence/agents/eaton_snmp/fence_eaton_snmp.py @@ -1,233 +1,233 @@ #!/usr/bin/python -tt # The Following agent has been tested on: # - Eaton ePDU Managed - SNMP v1 # EATON | Powerware ePDU model: Managed ePDU (PW104MA0UB99), firmware: 01.01.01 # - Eaton ePDU Switched - SNMP v1 # EATON | Powerware ePDU model: Switched ePDU (IPV3600), firmware: 2.0.K import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing_snmp import FencingSnmp #BEGIN_VERSION_GENERATION RELEASE_VERSION="Eaton SNMP fence agent" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION ### CONSTANTS ### # oid defining fence device OID_SYS_OBJECT_ID = '.1.3.6.1.2.1.1.2.0' ### GLOBAL VARIABLES ### # Device - see EatonManagedePDU, EatonSwitchedePDU device = None # Port ID port_id = None # Switch ID switch_id = None # Did we issue a set before get (to adjust OID with Switched ePDU) after_set = False # Classes describing Device params # Managed ePDU class EatonManagedePDU: status_oid = '.1.3.6.1.4.1.534.6.6.6.1.2.2.1.3.%d' control_oid = '.1.3.6.1.4.1.534.6.6.6.1.2.2.1.3.%d' outlet_table_oid = '.1.3.6.1.4.1.534.6.6.6.1.2.2.1.1' ident_str = "Eaton Managed ePDU" state_off = 0 state_on = 1 state_cycling = 2 # FIXME: not usable with fence-agents turn_off = 0 turn_on = 1 turn_cycle = 2 # FIXME: not usable with fence-agents has_switches = False # Switched ePDU (Pulizzi 2) # NOTE: sysOID reports "20677.1", while data are actually at "20677.2" class EatonSwitchedePDU: status_oid = '.1.3.6.1.4.1.20677.2.6.3.%d.0' control_oid = '.1.3.6.1.4.1.20677.2.6.2.%d.0' outlet_table_oid = '.1.3.6.1.4.1.20677.2.6.3' ident_str = "Eaton Switched ePDU" state_off = 2 state_on = 1 state_cycling = 0 # Note: this status doesn't exist on this device turn_off = 2 turn_on = 1 turn_cycle = 3 # FIXME: not usable with fence-agents has_switches = False ### FUNCTIONS ### -def eaton_set_device(conn, options): +def eaton_set_device(conn): global device agents_dir = {'.1.3.6.1.4.1.534.6.6.6':EatonManagedePDU, '.1.3.6.1.4.1.20677.1':EatonSwitchedePDU, '.1.3.6.1.4.1.20677.2':EatonSwitchedePDU } # First resolve type of Eaton eaton_type = conn.walk(OID_SYS_OBJECT_ID) if (not ((len(eaton_type)==1) and (agents_dir.has_key(eaton_type[0][1])))): eaton_type = [[None, None]] device = agents_dir[eaton_type[0][1]] conn.log_command("Trying %s"%(device.ident_str)) def eaton_resolv_port_id(conn, options): global port_id, switch_id if (device==None): - eaton_set_device(conn, options) + eaton_set_device(conn) # Restore the increment, that was removed in main for ePDU Managed if (device.ident_str == "Eaton Switched ePDU"): options["--plug"] = str(int(options["--plug"]) + 1) # Now we resolv port_id/switch_id if ((options["--plug"].isdigit()) and ((not device.has_switches) or (options["--switch"].isdigit()))): port_id = int(options["--plug"]) if (device.has_switches): switch_id = int(options["--switch"]) else: table = conn.walk(device.outlet_table_oid, 30) for x in table: if (x[1].strip('"')==options["--plug"]): t = x[0].split('.') if (device.has_switches): port_id = int(t[len(t)-1]) switch_id = int(t[len(t)-3]) else: if (device.ident_str == "Eaton Switched ePDU"): port_id = int(t[len(t)-3]) else: port_id = int(t[len(t)-1]) if (port_id==None): # Restore index offset, to provide a valid error output on Managed ePDU if (device.ident_str != "Eaton Switched ePDU"): options["--plug"] = str(int(options["--plug"]) + 1) fail_usage("Can't find port with name %s!"%(options["--plug"])) def get_power_status(conn, options): global port_id, after_set if (port_id==None): eaton_resolv_port_id(conn, options) # Ajust OID for Switched ePDU when the get is after a set if ((after_set == True) and (device.ident_str == "Eaton Switched ePDU")): port_id -= 1 after_set = False oid = ((device.has_switches) and device.status_oid%(switch_id, port_id) or device.status_oid%(port_id)) try: (oid, status)=conn.get(oid) if (status==str(device.state_on)): return "on" elif (status==str(device.state_off)): return "off" else: return None except: return None def set_power_status(conn, options): global port_id, after_set after_set = True if (port_id==None): eaton_resolv_port_id(conn, options) # Controls start at #2 on Switched ePDU, since #1 is the global command if (device.ident_str == "Eaton Switched ePDU"): port_id = int(port_id)+1 oid = ((device.has_switches) and device.control_oid%(switch_id, port_id) or device.control_oid%(port_id)) conn.set(oid,(options["--action"]=="on" and device.turn_on or device.turn_off)) def get_outlets_status(conn, options): outletCount = 0 result = {} if (device==None): - eaton_set_device(conn, options) + eaton_set_device(conn) res_ports = conn.walk(device.outlet_table_oid, 30) for x in res_ports: outletCount += 1 status = x[1] t = x[0].split('.') # Plug indexing start from zero, so we substract '1' from the # user's given plug number if (device.ident_str == "Eaton Managed ePDU"): port_num = str(int(((device.has_switches) and "%s:%s"%(t[len(t)-3], t[len(t)-1]) or "%s"%(t[len(t)-1]))) + 1) # Plug indexing start from zero, so we add '1' # for the user's exposed plug number port_name = str(int(x[1].strip('"')) + 1) port_status = "" result[port_num] = (port_name, port_status) else: # Switched ePDU do not propose an outletCount OID! # Invalid status (ie value == '0'), retrieved via the walk, # means the outlet is absent port_num = str(outletCount) port_name = str(outletCount) port_status = "" if (status != '0'): result[port_num] = (port_name, port_status) return result # Main agent method def main(): device_opt = [ "ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "community" ] atexit.register(atexit_handler) all_opt["switch"]["default"] = 1 all_opt["power_wait"]["default"] = 2 all_opt["snmp_version"]["default"] = "1" all_opt["community"]["default"] = "private" options = check_input(device_opt, process_input(device_opt)) # Plug indexing start from zero on ePDU Managed, so we substract '1' from # the user's given plug number. # For Switched ePDU, we will add this back again later. if ((options.has_key("--plug")) and (options["--plug"].isdigit())): options["--plug"] = str(int(options["--plug"]) - 1) docs = { } docs["shortdesc"] = "Fence agent for Eaton over SNMP" docs["longdesc"] = "fence_eaton_snmp is an I/O Fencing agent \ which can be used with the Eaton network power switch. It logs \ into a device via SNMP and reboots a specified outlet. It supports \ SNMP v1 and v3 with all combinations of authenticity/privacy settings." docs["vendorurl"] = "http://powerquality.eaton.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/eps/fence_eps.py b/fence/agents/eps/fence_eps.py index 0d9e573f..3a52b3c9 100644 --- a/fence/agents/eps/fence_eps.py +++ b/fence/agents/eps/fence_eps.py @@ -1,126 +1,128 @@ #!/usr/bin/python -tt # The Following Agent Has Been Tested On: # ePowerSwitch 8M+ version 1.0.0.4 import sys, re import httplib, base64, string, socket import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_LOGIN_DENIED, EC_TIMED_OUT, LOG_MODE_VERBOSE #BEGIN_VERSION_GENERATION RELEASE_VERSION="ePowerSwitch 8M+ (eps)" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION # Run command on EPS device. # @param options Device options # @param params HTTP GET parameters (without ?) def eps_run_command(options, params): try: # New http connection conn = httplib.HTTPConnection(options["--ip"]) request_str = "/"+options["--page"] if (params!=""): request_str += "?"+params logging.debug("GET %s\n" % request_str) conn.putrequest('GET', request_str) if (options.has_key("--username")): if (not options.has_key("--password")): options["--password"] = "" # Default is empty password # String for Authorization header auth_str = 'Basic ' + string.strip(base64.encodestring(options["--username"]+':'+options["--password"])) logging.debug("Authorization: %s\n" % auth_str) conn.putheader('Authorization', auth_str) conn.endheaders() response = conn.getresponse() logging.debug("%d %s\n"%(response.status, response.reason)) #Response != OK -> couldn't login if (response.status!=200): fail(EC_LOGIN_DENIED) result = response.read() logging.debug("%s \n" % result) conn.close() except socket.timeout: fail(EC_TIMED_OUT) except socket.error: fail(EC_LOGIN_DENIED) return result def get_power_status(conn, options): + del conn ret_val = eps_run_command(options,"") result = {} status = re.findall(r"p(\d{2})=(0|1)\s*\", ret_val.lower()) for out_num, out_stat in status: result[out_num] = ("",(out_stat=="1" and "on" or "off")) if (not (options["--action"] in ['monitor','list'])): if (not (options["--plug"] in result)): fail_usage("Failed: You have to enter existing physical plug!") else: return result[options["--plug"]][1] else: return result def set_power_status(conn, options): + del conn eps_run_command(options, "P%s=%s"%(options["--plug"], (options["--action"]=="on" and "1" or "0"))) # Define new option def eps_define_new_opts(): all_opt["hidden_page"] = { "getopt" : "c:", "longopt" : "page", "help":"-c, --page=[page] Name of hidden page (default hidden.htm)", "required" : "0", "shortdesc" : "Name of hidden page", "default" : "hidden.htm", "order": 1 } # Starting point of fence agent def main(): device_opt = [ "ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "hidden_page" ] atexit.register(atexit_handler) eps_define_new_opts() options = check_input(device_opt, process_input(device_opt)) docs = { } docs["shortdesc"] = "Fence agent for ePowerSwitch" docs["longdesc"] = "fence_eps is an I/O Fencing agent \ which can be used with the ePowerSwitch 8M+ power switch to fence \ connected machines. Fence agent works ONLY on 8M+ device, because \ this is only one, which has support for hidden page feature. \ \n.TP\n\ Agent basically works by connecting to hidden page and pass \ appropriate arguments to GET request. This means, that hidden \ page feature must be enabled and properly configured." docs["vendorurl"] = "http://www.epowerswitch.com" show_docs(options, docs) #Run fence action. Conn is None, beacause we always need open new http connection result = fence_action(None, options, set_power_status, get_power_status, get_power_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/rhevm/fence_rhevm.py b/fence/agents/rhevm/fence_rhevm.py index c8760d97..8efdc456 100644 --- a/fence/agents/rhevm/fence_rhevm.py +++ b/fence/agents/rhevm/fence_rhevm.py @@ -1,129 +1,133 @@ #!/usr/bin/python -tt import sys, re import pycurl, StringIO import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_STATUS, LOG_MODE_VERBOSE #BEGIN_VERSION_GENERATION RELEASE_VERSION="New RHEV-M Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="March, 2008" #END_VERSION_GENERATION RE_GET_ID = re.compile("(.*?)", re.IGNORECASE) RE_GET_NAME = re.compile("(.*?)", re.IGNORECASE) def get_power_status(conn, options): + del conn + ### Obtain real ID from name res = send_command(options, "vms/?search=name%3D" + options["--plug"]) result = RE_GET_ID.search(res) if (result == None): # Unable to obtain ID needed to access virtual machine fail(EC_STATUS) options["id"] = result.group(2) result = RE_STATUS.search(res) if (result == None): # We were able to parse ID so output is correct # in some cases it is possible that RHEV-M output does not # contain line. We can assume machine is OFF then return "off" else: status = result.group(1) if (status.lower() == "down"): return "off" else: return "on" def set_power_status(conn, options): + del conn action = { 'on' : "start", 'off' : "stop" }[options["--action"]] url = "vms/" + options["id"] + "/" + action send_command(options, url, "POST") def get_list(conn, options): + del conn outlets = { } try: res = send_command(options, "vms") lines = res.split("") c.setopt(pycurl.WRITEFUNCTION, b.write) c.perform() result = b.getvalue() logging.debug("%s\n" % command) logging.debug("%s\n" % result) return result def main(): device_opt = [ "ipaddr", "login", "passwd", "ssl", "notls", "web", "port" ] atexit.register(atexit_handler) all_opt["power_wait"]["default"] = "1" options = check_input(device_opt, process_input(device_opt)) docs = { } docs["shortdesc"] = "Fence agent for RHEV-M REST API" docs["longdesc"] = "fence_rhevm is an I/O Fencing agent which can be \ used with RHEV-M REST API to fence virtual machines." docs["vendorurl"] = "http://www.redhat.com" show_docs(options, docs) ## ## Fence operations #### result = fence_action(None, options, set_power_status, get_power_status, get_list) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/vmware/fence_vmware.py b/fence/agents/vmware/fence_vmware.py index f645650c..c9310fb1 100644 --- a/fence/agents/vmware/fence_vmware.py +++ b/fence/agents/vmware/fence_vmware.py @@ -1,340 +1,344 @@ #!/usr/bin/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, LOG_MODE_VERBOSE #BEGIN_VERSION_GENERATION RELEASE_VERSION="VMware Agent using VI Perl API and/or VIX vmrun command" REDHAT_COPYRIGHT="" BUILD_DATE="" #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_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 ((options.has_key("--vmware-datacenter")) and (vmware_internal_type==VMWARE_TYPE_ESX)): res += "--datacenter '%s' "% (quote_for_run(options["--vmware-datacenter"])) if (additional_params != ""): res += additional_params return res # Log message if user set verbose option def vmware_log(options, message): logging.debug("%s\n" % message) # 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, 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)): vmware_log(options, res_output) fail_usage("%s returned %s"% (options["--exec"], res_output)) else: vmware_log(options, 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(conn, options, add_vm_name): +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(conn, options): +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(conn, options, False) + 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(conn, options) + return vmware_get_outlets_vix(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) 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 (not options.has_key("--exec")): options["--exec"] = VMHELPER_COMMAND elif (options["--vmware_type"]=="server2"): vmware_internal_type = VMWARE_TYPE_SERVER2 if (not options.has_key("--exec")): options["--exec"] = VMRUN_COMMAND elif (options["--vmware_type"]=="server1"): vmware_internal_type = VMWARE_TYPE_SERVER1 if (not options.has_key("--exec")): 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) # 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()