diff --git a/fence/agents/apc/fence_apc.py b/fence/agents/apc/fence_apc.py index 1e86c07f..f7445835 100644 --- a/fence/agents/apc/fence_apc.py +++ b/fence/agents/apc/fence_apc.py @@ -1,271 +1,271 @@ #!/usr/bin/python -tt ##### ## ## The Following Agent Has Been Tested On: ## ## Model Firmware ## +---------------------------------------------+ ## AP7951 AOS v2.7.0, PDU APP v2.7.3 ## AP7941 AOS v3.5.7, PDU APP v3.5.6 ## AP9606 AOS v2.5.4, PDU APP v2.7.3 ## ## @note: ssh is very slow on AP79XX devices protocol (1) and ## cipher (des/blowfish) have to be defined ##### import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_STATUS #BEGIN_VERSION_GENERATION RELEASE_VERSION="New APC Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="March, 2008" #END_VERSION_GENERATION def get_power_status(conn, options): exp_result = 0 outlets = {} conn.send_eol("1") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) version = 0 admin = 0 switch = 0 if (None != re.compile('.* MasterSwitch plus.*', re.IGNORECASE | re.S).match(conn.before)): switch = 1 if (None != re.compile('.* MasterSwitch plus 2', re.IGNORECASE | re.S).match(conn.before)): if (0 == options.has_key("--switch")): fail_usage("Failed: You have to enter physical switch number") else: if (0 == options.has_key("--switch")): options["--switch"] = "1" if (None == re.compile('.*Outlet Management.*', re.IGNORECASE | re.S).match(conn.before)): version = 2 else: version = 3 if (None == re.compile('.*Outlet Control/Configuration.*', re.IGNORECASE | re.S).match(conn.before)): admin = 0 else: admin = 1 if switch == 0: if version == 2: if admin == 0: conn.send_eol("2") else: conn.send_eol("3") else: conn.send_eol("2") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) conn.send_eol("1") else: conn.send_eol(options["--switch"]) while True: exp_result = conn.log_expect(options, ["Press " ] + options["--command-prompt"], int(options["--shell-timeout"])) lines = conn.before.split("\n") show_re = re.compile(r'(^|\x0D)\s*(\d+)- (.*?)\s+(ON|OFF)\s*') - for x in lines: - res = show_re.search(x) + for line in lines: + res = show_re.search(line) if (res != None): outlets[res.group(2)] = (res.group(3), res.group(4)) conn.send_eol("") if exp_result != 0: break conn.send(chr(03)) conn.log_expect(options, "- Logout", int(options["--shell-timeout"])) conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) if ["list", "monitor"].count(options["--action"]) == 1: return outlets else: try: (_, status) = outlets[options["--plug"]] return status.lower().strip() except KeyError: fail(EC_STATUS) def set_power_status(conn, options): action = { 'on' : "1", 'off': "2" }[options["--action"]] conn.send_eol("1") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) version = 0 admin2 = 0 admin3 = 0 switch = 0 if (None != re.compile('.* MasterSwitch plus.*', re.IGNORECASE | re.S).match(conn.before)): switch = 1 ## MasterSwitch has different schema for on/off actions action = { 'on' : "1", 'off': "3" }[options["--action"]] if (None != re.compile('.* MasterSwitch plus 2', re.IGNORECASE | re.S).match(conn.before)): if (0 == options.has_key("--switch")): fail_usage("Failed: You have to enter physical switch number") else: if (0 == options.has_key("--switch")): options["--switch"] = 1 if (None == re.compile('.*Outlet Management.*', re.IGNORECASE | re.S).match(conn.before)): version = 2 else: version = 3 if (None == re.compile('.*Outlet Control/Configuration.*', re.IGNORECASE | re.S).match(conn.before)): admin2 = 0 else: admin2 = 1 if switch == 0: if version == 2: if admin2 == 0: conn.send_eol("2") else: conn.send_eol("3") else: conn.send_eol("2") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) if (None == re.compile('.*2- Outlet Restriction.*', re.IGNORECASE | re.S).match(conn.before)): admin3 = 0 else: admin3 = 1 conn.send_eol("1") else: conn.send_eol(options["--switch"]) while 0 == conn.log_expect(options, [ "Press " ] + options["--command-prompt"], int(options["--shell-timeout"])): conn.send_eol("") conn.send_eol(options["--plug"]+"") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) if switch == 0: if admin2 == 1: conn.send_eol("1") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) if admin3 == 1: conn.send_eol("1") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) else: conn.send_eol("1") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) conn.send_eol(action) conn.log_expect(options, "Enter 'YES' to continue or to cancel :", int(options["--shell-timeout"])) conn.send_eol("YES") conn.log_expect(options, "Press to continue...", int(options["--shell-timeout"])) conn.send_eol("") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) conn.send(chr(03)) conn.log_expect(options, "- Logout", int(options["--shell-timeout"])) conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) def get_power_status5(conn, options): outlets = {} conn.send_eol("olStatus all") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) lines = conn.before.split("\n") show_re = re.compile(r'^\s*(\d+): (.*): (On|Off)\s*$', re.IGNORECASE) - for x in lines: - res = show_re.search(x) + for line in lines: + res = show_re.search(line) if (res != None): outlets[res.group(1)] = (res.group(2), res.group(3)) if ["list", "monitor"].count(options["--action"]) == 1: return outlets else: try: (_, status) = outlets[options["--plug"]] return status.lower().strip() except KeyError: fail(EC_STATUS) def set_power_status5(conn, options): action = { 'on' : "olOn", 'off': "olOff" }[options["--action"]] conn.send_eol(action + " " + options["--plug"]) conn.log_expect(options, options["--command-prompt"], int(options["--power-timeout"])) def main(): device_opt = [ "ipaddr", "login", "passwd", "cmd_prompt", "secure", \ "port", "switch" ] atexit.register(atexit_handler) all_opt["cmd_prompt"]["default"] = [ "\n>", "\napc>" ] all_opt["ssh_options"]["default"] = "-1 -c blowfish" options = check_input(device_opt, process_input(device_opt)) docs = { } docs["shortdesc"] = "Fence agent for APC over telnet/ssh" docs["longdesc"] = "fence_apc is an I/O Fencing agent \ which can be used with the APC network power switch. It logs into device \ via telnet/ssh and reboots a specified outlet. Lengthy telnet/ssh connections \ should be avoided while a GFS cluster is running because the connection \ will block any necessary fencing actions." docs["vendorurl"] = "http://www.apc.com" show_docs(options, docs) ## Support for --plug [switch]:[plug] notation that was used before if (options.has_key("--plug") == 1) and (-1 != options["--plug"].find(":")): (switch, plug) = options["--plug"].split(":", 1) options["--switch"] = switch options["--plug"] = plug ## ## Operate the fencing device #### conn = fence_login(options) ## Detect firmware version (ASCII menu vs command-line interface) ## and continue with proper action #### result = -1 firmware_version = re.compile(r'\s*v(\d)*\.').search(conn.before) if (firmware_version != None) and (firmware_version.group(1) == "5"): result = fence_action(conn, options, set_power_status5, get_power_status5, get_power_status5) else: result = fence_action(conn, options, set_power_status, get_power_status, get_power_status) ## ## Logout from system ## ## In some special unspecified cases it is possible that ## connection will be closed before we run close(). This is not ## a problem because everything is checked before. ###### try: conn.send_eol("4") conn.close() except: pass sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/brocade/fence_brocade.py b/fence/agents/brocade/fence_brocade.py index a60355d8..35082ee7 100644 --- a/fence/agents/brocade/fence_brocade.py +++ b/fence/agents/brocade/fence_brocade.py @@ -1,88 +1,88 @@ #!/usr/bin/python -tt import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_STATUS #BEGIN_VERSION_GENERATION RELEASE_VERSION="New Brocade Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="March, 20013" #END_VERSION_GENERATION def get_power_status(conn, options): conn.send_eol("portCfgShow " + options["--plug"]) conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) show_re = re.compile(r'^\s*Persistent Disable\s*(ON|OFF)\s*$', re.IGNORECASE) lines = conn.before.split("\n") - for x in lines: - res = show_re.search(x) + for line in lines: + res = show_re.search(line) if (res != None): # We queried if it is disabled, so we have to negate answer if res.group(1) == "ON": return "off" else: return "on" fail(EC_STATUS) def set_power_status(conn, options): action = { 'on' : "portCfgPersistentEnable", 'off': "portCfgPersistentDisable" }[options["--action"]] conn.send_eol(action + " " + options["--plug"]) conn.log_expect(options, options["--command-prompt"], int(options["--power-timeout"])) def main(): device_opt = [ "ipaddr", "login", "passwd", "cmd_prompt", "secure", "port", "fabric_fencing" ] atexit.register(atexit_handler) all_opt["cmd_prompt"]["default"] = [ "> " ] options = check_input(device_opt, process_input(device_opt)) options["eol"] = "\n" docs = { } docs["shortdesc"] = "Fence agent for HP Brocade over telnet/ssh" docs["longdesc"] = "fence_brocade is an I/O Fencing agent which can be used with Brocade FC switches. \ It logs into a Brocade switch via telnet and disables a specified port. Disabling the port which a machine is \ connected to effectively fences that machine. Lengthy telnet connections to the switch should be avoided while \ a GFS cluster is running because the connection will block any necessary fencing actions. \ \ After a fence operation has taken place the fenced machine can no longer connect to the Brocade FC switch. \ When the fenced machine is ready to be brought back into the GFS cluster (after reboot) the port on the Brocade \ FC switch needs to be enabled. This can be done by running fence_brocade and specifying the enable action" docs["vendorurl"] = "http://www.brocade.com" show_docs(options, docs) ## ## Operate the fencing device #### conn = fence_login(options) result = fence_action(conn, options, set_power_status, get_power_status, None) ## ## Logout from system ## ## In some special unspecified cases it is possible that ## connection will be closed before we run close(). This is not ## a problem because everything is checked before. ###### try: conn.send_eol("exit") conn.close() except: pass 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 a9ca8fe6..3ef7c7b3 100644 --- a/fence/agents/cisco_ucs/fence_cisco_ucs.py +++ b/fence/agents/cisco_ucs/fence_cisco_ucs.py @@ -1,165 +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/lib/check_used_options.py b/fence/agents/lib/check_used_options.py index ce74fa98..7f821b1d 100755 --- a/fence/agents/lib/check_used_options.py +++ b/fence/agents/lib/check_used_options.py @@ -1,65 +1,65 @@ #!/usr/bin/python -tt ## Check if fence agent uses only options["--??"] which are defined in fencing library or ## fence agent itself ## ## Usage: ./check_used_options.py fence-agent (e.g. lpar/fence_lpar.py) ## import sys, re sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import all_opt def main(): agent = sys.argv[1] available = { } ## all_opt from fencing library are imported for k in all_opt.keys(): if all_opt[k].has_key("longopt"): available["--" + all_opt[k]["longopt"]] = True ## add UUID which is derived automatically from --plug if possible available["--uuid"] = True ## all_opt defined in fence agent are found agent_file = open(agent) opt_re = re.compile(r"\s*all_opt\[\"([^\"]*)\"\] = {") opt_longopt_re = re.compile(r"\s*\"longopt\" : \"([^\"]*)\"") in_opt = False for line in agent_file: if opt_re.search(line) != None: in_opt = True if in_opt and opt_longopt_re.search(line) != None: available["--" + opt_longopt_re.search(line).group(1)] = True in_opt = False ## check if all options are defined agent_file = open(agent) option_use_re = re.compile(r"options\[\"(-[^\"]*)\"\]") option_has_re = re.compile(r"options.has_key\(\"(-[^\"]*)\"\)") counter = 0 without_errors = True for line in agent_file: counter += 1 - for x in option_use_re.findall(line): - if not available.has_key(x): + for option in option_use_re.findall(line): + if not available.has_key(option): print "ERROR on line %d in %s: option %s is not defined" % (counter, agent, option_use_re.search(line).group(1)) without_errors = False - for x in option_has_re.findall(line): - if not available.has_key(x): + for option in option_has_re.findall(line): + if not available.has_key(option): print "ERROR on line %d in %s: option %s is not defined" % (counter, agent, option_has_re.search(line).group(1)) without_errors = False if without_errors: sys.exit(0) else: sys.exit(1) if __name__ == "__main__": main() diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py index d5c9986f..11985d2d 100644 --- a/fence/agents/lib/fencing.py.py +++ b/fence/agents/lib/fencing.py.py @@ -1,1096 +1,1096 @@ #!/usr/bin/python -tt import sys, getopt, time, os, uuid, pycurl, stat import pexpect, re, atexit, syslog import logging import __main__ ## do not add code here. #BEGIN_VERSION_GENERATION RELEASE_VERSION = "New fence lib agent - test release on steroids" REDHAT_COPYRIGHT = "" BUILD_DATE = "March, 2008" #END_VERSION_GENERATION __all__ = [ 'atexit_handler', 'check_input', 'process_input', 'all_opt', 'show_docs', 'fence_login', 'fence_action' ] LOG_MODE_VERBOSE = 100 LOG_MODE_QUIET = 0 EC_GENERIC_ERROR = 1 EC_BAD_ARGS = 2 EC_LOGIN_DENIED = 3 EC_CONNECTION_LOST = 4 EC_TIMED_OUT = 5 EC_WAITING_ON = 6 EC_WAITING_OFF = 7 EC_STATUS = 8 EC_STATUS_HMC = 9 EC_PASSWORD_MISSING = 10 EC_INVALID_PRIVILEGES = 11 TELNET_PATH = "/usr/bin/telnet" SSH_PATH = "/usr/bin/ssh" SSL_PATH = "@GNUTLSCLI_PATH@" SUDO_PATH = "/usr/bin/sudo" all_opt = { "help" : { "getopt" : "h", "longopt" : "help", "help" : "-h, --help Display this help and exit", "required" : "0", "shortdesc" : "Display help and exit", "order" : 54 }, "version" : { "getopt" : "V", "longopt" : "version", "help" : "-V, --version Output version information and exit", "required" : "0", "shortdesc" : "Display version information and exit", "order" : 53 }, "verbose" : { "getopt" : "v", "longopt" : "verbose", "help" : "-v, --verbose Verbose mode", "required" : "0", "shortdesc" : "Verbose mode", "order" : 51 }, "debug" : { "getopt" : "D:", "longopt" : "debug-file", "help" : "-D, --debug-file=[debugfile] Debugging to output file", "required" : "0", "shortdesc" : "Write debug information to given file", "order" : 52 }, "delay" : { "getopt" : "f:", "longopt" : "delay", "help" : "--delay=[seconds] Wait X seconds before fencing is started", "required" : "0", "shortdesc" : "Wait X seconds before fencing is started", "default" : "0", "order" : 200 }, "agent" : { "getopt" : "", "help" : "", "order" : 1 }, "web" : { "getopt" : "", "help" : "", "order" : 1 }, "action" : { "getopt" : "o:", "longopt" : "action", "help" : "-o, --action=[action] Action: status, reboot (default), off or on", "required" : "1", "shortdesc" : "Fencing Action", "default" : "reboot", "order" : 1 }, "fabric_fencing" : { "getopt" : "", "help" : "", "order" : 1 }, "ipaddr" : { "getopt" : "a:", "longopt" : "ip", "help" : "-a, --ip=[ip] IP address or hostname of fencing device", "required" : "1", "shortdesc" : "IP Address or Hostname", "order" : 1 }, "ipport" : { "getopt" : "u:", "longopt" : "ipport", "help" : "-u, --ipport=[port] TCP/UDP port to use", "required" : "0", "shortdesc" : "TCP/UDP port to use for connection with device", "order" : 1 }, "login" : { "getopt" : "l:", "longopt" : "username", "help" : "-l, --username=[name] Login name", "required" : "?", "shortdesc" : "Login Name", "order" : 1 }, "no_login" : { "getopt" : "", "help" : "", "order" : 1 }, "no_password" : { "getopt" : "", "help" : "", "order" : 1 }, "no_port" : { "getopt" : "", "help" : "", "order" : 1 }, "passwd" : { "getopt" : "p:", "longopt" : "password", "help" : "-p, --password=[password] Login password or passphrase", "required" : "0", "shortdesc" : "Login password or passphrase", "order" : 1 }, "passwd_script" : { "getopt" : "S:", "longopt" : "password-script", "help" : "-S, --password-script=[script] Script to run to retrieve password", "required" : "0", "shortdesc" : "Script to retrieve password", "order" : 1 }, "identity_file" : { "getopt" : "k:", "longopt" : "identity-file", "help" : "-k, --identity-file=[filename] Identity file (private key) for ssh ", "required" : "0", "shortdesc" : "Identity file for ssh", "order" : 1 }, "cmd_prompt" : { "getopt" : "c:", "longopt" : "command-prompt", "help" : "-c, --command-prompt=[prompt] Force Python regex for command prompt", "shortdesc" : "Force Python regex for command prompt", "required" : "0", "order" : 1 }, "secure" : { "getopt" : "x", "longopt" : "ssh", "help" : "-x, --ssh Use ssh connection", "shortdesc" : "SSH connection", "required" : "0", "order" : 1 }, "ssh_options" : { "getopt" : "X:", "longopt" : "ssh-options", "help" : "--ssh-options=[options] SSH options to use", "shortdesc" : "SSH options to use", "required" : "0", "order" : 1 }, "ssl" : { "getopt" : "z", "longopt" : "ssl", "help" : "-z, --ssl Use ssl connection", "required" : "0", "shortdesc" : "SSL connection", "order" : 1 }, "notls" : { "getopt" : "t", "longopt" : "notls", "help" : "-t, --notls " "Disable TLS negotiation and force SSL3.0.\n" " " "This should only be used for devices that do not support TLS1.0 and up.", "required" : "0", "shortdesc" : "Disable TLS negotiation", "order" : 1 }, "port" : { "getopt" : "n:", "longopt" : "plug", "help" : "-n, --plug=[id] Physical plug number on device, UUID or\n" + " identification of machine", "required" : "1", "shortdesc" : "Physical plug number, name of virtual machine or UUID", "order" : 1 }, "switch" : { "getopt" : "s:", "longopt" : "switch", "help" : "-s, --switch=[id] Physical switch number on device", "required" : "0", "shortdesc" : "Physical switch number on device", "order" : 1 }, "exec" : { "getopt" : "e:", "longopt" : "exec", "help" : "-e, --exec=[command] Command to execute", "required" : "0", "shortdesc" : "Command to execute", "order" : 1 }, "vmware_type" : { "getopt" : "d:", "longopt" : "vmware_type", "help" : "-d, --vmware_type=[type] Type of VMware to connect", "required" : "0", "shortdesc" : "Type of VMware to connect", "order" : 1 }, "vmware_datacenter" : { "getopt" : "s:", "longopt" : "vmware-datacenter", "help" : "-s, --vmware-datacenter=[dc] VMWare datacenter filter", "required" : "0", "shortdesc" : "Show only machines in specified datacenter", "order" : 2 }, "snmp_version" : { "getopt" : "d:", "longopt" : "snmp-version", "help" : "-d, --snmp-version=[version] Specifies SNMP version to use", "required" : "0", "shortdesc" : "Specifies SNMP version to use (1,2c,3)", "choices" : [ "1", "2c", "3" ], "order" : 1 }, "community" : { "getopt" : "c:", "longopt" : "community", "help" : "-c, --community=[community] Set the community string", "required" : "0", "shortdesc" : "Set the community string", "order" : 1}, "snmp_auth_prot" : { "getopt" : "b:", "longopt" : "snmp-auth-prot", "help" : "-b, --snmp-auth-prot=[prot] Set authentication protocol (MD5|SHA)", "required" : "0", "shortdesc" : "Set authentication protocol (MD5|SHA)", "choices" : [ "MD5" , "SHA" ], "order" : 1}, "snmp_sec_level" : { "getopt" : "E:", "longopt" : "snmp-sec-level", "help" : "-E, --snmp-sec-level=[level] Set security level\n"+ " (noAuthNoPriv|authNoPriv|authPriv)", "required" : "0", "shortdesc" : "Set security level (noAuthNoPriv|authNoPriv|authPriv)", "choices" : [ "noAuthNoPriv", "authNoPriv", "authPriv" ], "order" : 1}, "snmp_priv_prot" : { "getopt" : "B:", "longopt" : "snmp-priv-prot", "help" : "-B, --snmp-priv-prot=[prot] Set privacy protocol (DES|AES)", "required" : "0", "shortdesc" : "Set privacy protocol (DES|AES)", "choices" : [ "DES", "AES" ], "order" : 1}, "snmp_priv_passwd" : { "getopt" : "P:", "longopt" : "snmp-priv-passwd", "help" : "-P, --snmp-priv-passwd=[pass] Set privacy protocol password", "required" : "0", "shortdesc" : "Set privacy protocol password", "order" : 1}, "snmp_priv_passwd_script" : { "getopt" : "R:", "longopt" : "snmp-priv-passwd-script", "help" : "-R, --snmp-priv-passwd-script Script to run to retrieve privacy password", "required" : "0", "shortdesc" : "Script to run to retrieve privacy password", "order" : 1}, "inet4_only" : { "getopt" : "4", "longopt" : "inet4-only", "help" : "-4, --inet4-only Forces agent to use IPv4 addresses only", "required" : "0", "shortdesc" : "Forces agent to use IPv4 addresses only", "order" : 1 }, "inet6_only" : { "getopt" : "6", "longopt" : "inet6-only", "help" : "-6, --inet6-only Forces agent to use IPv6 addresses only", "required" : "0", "shortdesc" : "Forces agent to use IPv6 addresses only", "order" : 1 }, "separator" : { "getopt" : "C:", "longopt" : "separator", "help" : "-C, --separator=[char] Separator for CSV created by 'list' operation", "default" : ",", "required" : "0", "shortdesc" : "Separator for CSV created by operation list", "order" : 100 }, "login_timeout" : { "getopt" : "y:", "longopt" : "login-timeout", "help" : "--login-timeout=[seconds] Wait X seconds for cmd prompt after login", "default" : "5", "required" : "0", "shortdesc" : "Wait X seconds for cmd prompt after login", "order" : 200 }, "shell_timeout" : { "getopt" : "Y:", "longopt" : "shell-timeout", "help" : "--shell-timeout=[seconds] Wait X seconds for cmd prompt after issuing command", "default" : "3", "required" : "0", "shortdesc" : "Wait X seconds for cmd prompt after issuing command", "order" : 200 }, "power_timeout" : { "getopt" : "g:", "longopt" : "power-timeout", "help" : "--power-timeout=[seconds] Test X seconds for status change after ON/OFF", "default" : "20", "required" : "0", "shortdesc" : "Test X seconds for status change after ON/OFF", "order" : 200 }, "power_wait" : { "getopt" : "G:", "longopt" : "power-wait", "help" : "--power-wait=[seconds] Wait X seconds after issuing ON/OFF", "default" : "0", "required" : "0", "shortdesc" : "Wait X seconds after issuing ON/OFF", "order" : 200 }, "missing_as_off" : { "getopt" : "M", "longopt" : "missing-as-off", "help" : "--missing-as-off Missing port returns OFF instead of failure", "required" : "0", "shortdesc" : "Missing port returns OFF instead of failure", "order" : 200 }, "retry_on" : { "getopt" : "F:", "longopt" : "retry-on", "help" : "--retry-on=[attempts] Count of attempts to retry power on", "default" : "1", "required" : "0", "shortdesc" : "Count of attempts to retry power on", "order" : 201 }, "session_url" : { "getopt" : "s:", "longopt" : "session-url", "help" : "-s, --session-url URL to connect to XenServer on", "required" : "1", "shortdesc" : "The URL of the XenServer host.", "order" : 1}, "sudo" : { "getopt" : "d", "longopt" : "use-sudo", "help" : "--use-sudo Use sudo (without password) when calling 3rd party software", "required" : "0", "shortdesc" : "Use sudo (without password) when calling 3rd party sotfware.", "order" : 205}, "method" : { "getopt" : "m:", "longopt" : "method", "help" : "-m, --method=[method] Method to fence (onoff|cycle) (Default: onoff)", "required" : "0", "shortdesc" : "Method to fence (onoff|cycle)", "default" : "onoff", "choices" : [ "onoff", "cycle" ], "order" : 1} } # options which are added automatically if 'key' is encountered ("default" is always added) DEPENDENCY_OPT = { "default" : [ "help", "debug", "verbose", "version", "action", "agent", \ "power_timeout", "shell_timeout", "login_timeout", "power_wait", "retry_on", "delay" ], "passwd" : [ "passwd_script" ], "secure" : [ "identity_file", "ssh_options" ], "ipaddr" : [ "ipport", "inet4_only", "inet6_only" ], "port" : [ "separator" ], "community" : [ "snmp_auth_prot", "snmp_sec_level", "snmp_priv_prot", \ "snmp_priv_passwd", "snmp_priv_passwd_script" ] } class fspawn(pexpect.spawn): def __init__(self, options, command): logging.info("Running command: %s" % command) pexpect.spawn.__init__(self, command) self.opt = options def log_expect(self, options, pattern, timeout): result = self.expect(pattern, timeout) logging.debug("Received: %s" % (self.before + self.after)) return result def send(self, message): logging.debug("Sent: %s" % message) pexpect.spawn.send(self,message) # send EOL according to what was detected in login process (telnet) def send_eol(self, message): self.send(message + self.opt["eol"]) def atexit_handler(): try: sys.stdout.close() os.close(1) except IOError: logging.error("%s failed to close standard output\n" % (sys.argv[0])) syslog.syslog(syslog.LOG_ERR, "Failed to close standard output") sys.exit(EC_GENERIC_ERROR) def add_dependency_options(options): - ## Add options which are available for every fence agent + ## Add also options which are available for every fence agent added_opt = [] - for x in options + ["default"]: - if DEPENDENCY_OPT.has_key(x): - added_opt.extend([y for y in DEPENDENCY_OPT[x] if options.count(y) == 0]) + for opt in options + ["default"]: + if DEPENDENCY_OPT.has_key(opt): + added_opt.extend([y for y in DEPENDENCY_OPT[opt] if options.count(y) == 0]) return added_opt def fail_usage(message = ""): if len(message) > 0: logging.error("%s\n" % message) logging.error("Please use '-h' for usage\n") sys.exit(EC_GENERIC_ERROR) def fail(error_code): message = { EC_LOGIN_DENIED : "Unable to connect/login to fencing device", EC_CONNECTION_LOST : "Connection lost", EC_TIMED_OUT : "Connection timed out", EC_WAITING_ON : "Failed: Timed out waiting to power ON", EC_WAITING_OFF : "Failed: Timed out waiting to power OFF", EC_STATUS : "Failed: Unable to obtain correct plug status or plug is not available", EC_STATUS_HMC : "Failed: Either unable to obtain correct plug status, " "partition is not available or incorrect HMC version used", EC_PASSWORD_MISSING : "Failed: You have to set login password", EC_INVALID_PRIVILEGES : "Failed: The user does not have the correct privileges to do the requested action." }[error_code] + "\n" logging.error("%s\n" % message) syslog.syslog(syslog.LOG_ERR, message) sys.exit(EC_GENERIC_ERROR) def usage(avail_opt): print "Usage:" print "\t" + os.path.basename(sys.argv[0]) + " [options]" print "Options:" sorted_list = [ (key, all_opt[key]) for key in avail_opt ] sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"])) for key, value in sorted_list: if len(value["help"]) != 0: print " " + value["help"] def metadata(avail_opt, options, docs): # avail_opt has to be unique, if there are duplicities then they should be removed sorted_list = [ (key, all_opt[key]) for key in list(set(avail_opt)) ] sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"])) print "" print "" if "symlink" in docs: for (symlink, desc) in docs["symlink"]: print "" print "" + docs["longdesc"] + "" if docs.has_key("vendorurl"): print "" + docs["vendorurl"] + "" print "" for option, _ in sorted_list: if all_opt[option].has_key("shortdesc"): print "\t" default = "" if all_opt[option].has_key("default"): default = str(all_opt[option]["default"]) elif options.has_key("--" + all_opt[option]["longopt"]) and all_opt[option]["getopt"].endswith(":"): if options["--" + all_opt[option]["longopt"]]: try: default = options["--" + all_opt[option]["longopt"]] except TypeError: ## @todo/@note: Currently there is no clean way how to handle lists ## we can create a string from it but we can't set it on command line default = str(options["--" + all_opt[option]["longopt"]]) elif options.has_key("--" + all_opt[option]["longopt"]): default = "true" if default: default = default.replace("&", "&" ) default = default.replace('"', """ ) default = default.replace('<', "<" ) default = default.replace('>', ">" ) default = default.replace("'", "'" ) default = "default=\"" + default + "\" " mixed = all_opt[option]["help"] ## split it between option and help text res = re.compile(r"^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed) if (None != res): mixed = res.group(1) mixed = mixed.replace("<", "<").replace(">", ">") print "\t\t" if all_opt[option].has_key("choices"): print "\t\t" for choice in all_opt[option]["choices"]: print "\t\t\t" elif all_opt[option]["getopt"].count(":") > 0: print "\t\t" else: print "\t\t" print "\t\t" + all_opt[option]["shortdesc"] + "" print "\t" print "" print "" if avail_opt.count("fabric_fencing") == 1: ## do 'unfence' at the start print "\t" else: print "\t" print "\t" if avail_opt.count("fabric_fencing") == 0: print "\t" print "\t" print "\t" print "\t" print "\t" print "" print "" def process_input(avail_opt): avail_opt.extend(add_dependency_options(avail_opt)) ## ## Set standard environment ##### os.putenv("LANG", "C") os.putenv("LC_ALL", "C") ## ## Prepare list of options for getopt ##### getopt_string = "" longopt_list = [ ] for k in avail_opt: if all_opt.has_key(k): getopt_string += all_opt[k]["getopt"] else: fail_usage("Parse error: unknown option '"+k+"'") if all_opt.has_key(k) and all_opt[k].has_key("longopt"): if all_opt[k]["getopt"].endswith(":"): longopt_list.append(all_opt[k]["longopt"] + "=") else: longopt_list.append(all_opt[k]["longopt"]) ## ## Read options from command line or standard input ##### if len(sys.argv) > 1: try: opt, _args = getopt.gnu_getopt(sys.argv[1:], getopt_string, longopt_list) except getopt.GetoptError, error: fail_usage("Parse error: " + error.msg) ## Transform short getopt to long one which are used in fencing agents ##### old_opt = opt opt = { } for o in dict(old_opt).keys(): if o.startswith("--"): for x in all_opt.keys(): if all_opt[x].has_key("longopt") and "--" + all_opt[x]["longopt"] == o: opt["--" + all_opt[x]["longopt"]] = dict(old_opt)[o] else: for x in all_opt.keys(): if x in avail_opt and all_opt[x].has_key("getopt") and all_opt[x].has_key("longopt") and \ ("-" + all_opt[x]["getopt"] == o or "-" + all_opt[x]["getopt"].rstrip(":") == o): opt["--" + all_opt[x]["longopt"]] = dict(old_opt)[o] opt[o] = dict(old_opt)[o] ## Compatibility Layer ##### z = dict(opt) if z.has_key("--plug") == 1: z["-m"] = z["--plug"] opt = z ## ##### else: opt = { } name = "" for line in sys.stdin.readlines(): line = line.strip() if ((line.startswith("#")) or (len(line) == 0)): continue (name, value) = (line + "=").split("=", 1) value = value[:-1] if avail_opt.count(name) == 0: logging.warning("Parse error: Ignoring unknown option '%s'\n" % line) syslog.syslog(syslog.LOG_WARNING, "Parse error: Ignoring unknown option '"+line) continue if all_opt[name]["getopt"].endswith(":"): opt["--"+all_opt[name]["longopt"].rstrip(":")] = value elif value.lower() in [ "1", "yes", "on", "true" ]: opt["--"+all_opt[name]["longopt"]] = "1" return opt ## ## This function checks input and answers if we want to have same answers ## in each of the fencing agents. It looks for possible errors and run ## password script to set a correct password ###### def check_input(device_opt, opt): device_opt.extend(add_dependency_options(device_opt)) options = dict(opt) options["device_opt"] = device_opt ## Set requirements that should be included in metadata ##### if device_opt.count("login") and device_opt.count("no_login") == 0: all_opt["login"]["required"] = "1" else: all_opt["login"]["required"] = "0" if device_opt.count("fabric_fencing"): all_opt["action"]["default"] = "off" all_opt["action"]["help"] = "-o, --action=[action] Action: status, off (default) or on" ## Set default values ##### for opt in device_opt: if all_opt[opt].has_key("default"): getopt_long = "--" + all_opt[opt]["longopt"] if 0 == options.has_key(getopt_long): options[getopt_long] = all_opt[opt]["default"] if device_opt.count("ipport"): if options.has_key("--ipport"): all_opt["ipport"]["help"] = "-u, --ipport=[port] " + \ "TCP/UDP port to use (default " + options["--ipport"] +")" elif device_opt.count("snmp_version"): all_opt["ipport"]["default"] = "161" all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default 161)" elif options.has_key("--ssh"): all_opt["ipport"]["default"] = 22 all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default 22)" elif options.has_key("--ssl"): all_opt["ipport"]["default"] = 443 all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default 443)" elif device_opt.count("web"): all_opt["ipport"]["default"] = 80 if device_opt.count("ssl") == 0: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default 80)" else: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use\n\ (default 80, 443 if --ssl option is used)" else: all_opt["ipport"]["default"] = 23 if device_opt.count("secure") == 0: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default 23)" else: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use\n\ (default 23, 22 if --ssh option is used)" ## In special cases (show help, metadata or version) we don't need to check anything ##### if options.has_key("--help") or options.has_key("--version") or \ (options.has_key("--action") and options["--action"].lower() == "metadata"): return options options["--action"] = options["--action"].lower() if options.has_key("--verbose"): logging.getLogger().setLevel(logging.DEBUG) acceptable_actions = [ "on", "off", "status", "list", "monitor" ] if 1 == device_opt.count("fabric_fencing"): ## Compatibility layer ##### acceptable_actions.extend(["enable", "disable"]) else: acceptable_actions.extend(["reboot"]) if 0 == acceptable_actions.count(options["--action"]): fail_usage("Failed: Unrecognised action '" + options["--action"] + "'") ## Compatibility layer ##### if options["--action"] == "enable": options["--action"] = "on" if options["--action"] == "disable": options["--action"] = "off" ## automatic detection and set of valid UUID from --plug if (0 == options.has_key("--username")) and \ device_opt.count("login") and (device_opt.count("no_login") == 0): fail_usage("Failed: You have to set login name") if device_opt.count("ipaddr") and 0 == options.has_key("--ip") and 0 == options.has_key("--managed"): fail_usage("Failed: You have to enter fence address") if (device_opt.count("no_password") == 0): if 0 == device_opt.count("identity_file"): if 0 == (options.has_key("--password") or options.has_key("--password-script")): fail_usage("Failed: You have to enter password or password script") else: if 0 == (options.has_key("--password") or \ options.has_key("--password-script") or options.has_key("--identity-file")): fail_usage("Failed: You have to enter password, password script or identity file") if 0 == options.has_key("--ssh") and 1 == options.has_key("--identity-file"): fail_usage("Failed: You have to use identity file together with ssh connection (-x)") if 1 == options.has_key("--identity-file"): if 0 == os.path.isfile(options["--identity-file"]): fail_usage("Failed: Identity file " + options["--identity-file"] + " does not exist") if (0 == ["list", "monitor"].count(options["--action"].lower())) and \ 0 == options.has_key("--plug") and device_opt.count("port") and device_opt.count("no_port") == 0: fail_usage("Failed: You have to enter plug number or machine identification") if options.has_key("--password-script"): options["--password"] = os.popen(options["--password-script"]).read().rstrip() if options.has_key("--debug-file"): try: fh = logging.FileHandler(options["--debug-file"]) fh.setLevel(logging.DEBUG) logging.getLogger().addHandler(fh) except IOError: logging.error("Unable to create file %s" % options["--debug-file"]) fail_usage("Failed: Unable to create file " + options["--debug-file"]) if options.has_key("--snmp-priv-passwd-script"): options["--snmp-priv-passwd"] = os.popen(options["--snmp-priv-passwd-script"]).read().rstrip() if options.has_key("--ipport") == False: if options.has_key("--ssh"): options["--ipport"] = 22 elif options.has_key("--ssl"): options["--ipport"] = 443 elif device_opt.count("web"): options["--ipport"] = 80 else: options["--ipport"] = 23 if options.has_key("--plug") and len(options["--plug"].split(",")) > 1 and \ options.has_key("--method") and options["--method"] == "cycle": fail_usage("Failed: Cannot use --method cycle for more than 1 plug") for opt in device_opt: if all_opt[opt].has_key("choices"): longopt = "--" + all_opt[opt]["longopt"] possible_values_upper = map (lambda y : y.upper(), all_opt[opt]["choices"]) if options.has_key(longopt): options[longopt] = options[longopt].upper() if not options["--" + all_opt[opt]["longopt"]] in possible_values_upper: fail_usage("Failed: You have to enter a valid choice " + \ "for %s from the valid values: %s" % \ ("--" + all_opt[opt]["longopt"] , str(all_opt[opt]["choices"]))) return options def wait_power_status(tn, options, get_power_fn): for dummy in xrange(int(options["--power-timeout"])): if get_multi_power_fn(tn, options, get_power_fn) != options["--action"]: time.sleep(1) else: return 1 return 0 ## Obtain a power status from possibly more than one plug ## "on" is returned if at least one plug is ON ###### def get_multi_power_fn(tn, options, get_power_fn): status = "off" if options.has_key("--plugs"): for plug in options["--plugs"]: try: options["--uuid"] = str(uuid.UUID(plug)) except ValueError: pass except KeyError: pass options["--plug"] = plug plug_status = get_power_fn(tn, options) if plug_status != "off": status = plug_status else: status = get_power_fn(tn, options) return status def set_multi_power_fn(tn, options, set_power_fn): if options.has_key("--plugs"): for plug in options["--plugs"]: try: options["--uuid"] = str(uuid.UUID(plug)) except ValueError: pass except KeyError: pass options["--plug"] = plug set_power_fn(tn, options) else: set_power_fn(tn, options) def show_docs(options, docs = None): device_opt = options["device_opt"] if docs == None: docs = { } docs["shortdesc"] = "Fence agent" docs["longdesc"] = "" ## Process special options (and exit) ##### if options.has_key("--help"): usage(device_opt) sys.exit(0) if options.has_key("--action") and options["--action"].lower() == "metadata": metadata(device_opt, options, docs) sys.exit(0) if options.has_key("--version"): print __main__.RELEASE_VERSION, __main__.BUILD_DATE print __main__.REDHAT_COPYRIGHT sys.exit(0) def fence_action(tn, options, set_power_fn, get_power_fn, get_outlet_list = None, reboot_cycle_fn = None): result = 0 try: if options.has_key("--plug"): options["--plugs"] = options["--plug"].split(",") ## Process options that manipulate fencing device ##### if (options["--action"] == "list") and 0 == options["device_opt"].count("port"): print "N/A" return elif (options["--action"] == "list" and get_outlet_list == None): ## @todo: exception? ## This is just temporal solution, we will remove default value ## None as soon as all existing agent will support this operation print "NOTICE: List option is not working on this device yet" return elif (options["--action"] == "list") or \ ((options["--action"] == "monitor") and 1 == options["device_opt"].count("port")): outlets = get_outlet_list(tn, options) ## keys can be numbers (port numbers) or strings (names of VM) - for o in outlets.keys(): - (alias, status) = outlets[o] + for outlet_id in outlets.keys(): + (alias, status) = outlets[outlet_id] if options["--action"] != "monitor": - print o + options["--separator"] + alias + print outled_id + options["--separator"] + alias return status = get_multi_power_fn(tn, options, get_power_fn) if status != "on" and status != "off": fail(EC_STATUS) if options["--action"] == "on": if status == "on": print "Success: Already ON" else: power_on = False for _ in range(1, 1 + int(options["--retry-on"])): set_multi_power_fn(tn, options, set_power_fn) time.sleep(int(options["--power-wait"])) if wait_power_status(tn, options, get_power_fn): power_on = True break if power_on: print "Success: Powered ON" else: fail(EC_WAITING_ON) elif options["--action"] == "off": if status == "off": print "Success: Already OFF" else: set_multi_power_fn(tn, options, set_power_fn) time.sleep(int(options["--power-wait"])) if wait_power_status(tn, options, get_power_fn): print "Success: Powered OFF" else: fail(EC_WAITING_OFF) elif options["--action"] == "reboot": power_on = False if options.has_key("--method") and options["--method"].lower() == "cycle" and reboot_cycle_fn is not None: for _ in range(1, 1 + int(options["--retry-on"])): if reboot_cycle_fn(tn, options): power_on = True break if not power_on: fail(EC_TIMED_OUT) else: if status != "off": options["--action"] = "off" set_multi_power_fn(tn, options, set_power_fn) time.sleep(int(options["--power-wait"])) if wait_power_status(tn, options, get_power_fn) == 0: fail(EC_WAITING_OFF) options["--action"] = "on" try: for _ in range(1, 1 + int(options["--retry-on"])): set_multi_power_fn(tn, options, set_power_fn) time.sleep(int(options["--power-wait"])) if wait_power_status(tn, options, get_power_fn) == 1: power_on = True break except Exception, ex: # an error occured during power ON phase in reboot # fence action was completed succesfully even in that case logging.error("%s\n", str(ex)) syslog.syslog(syslog.LOG_NOTICE, str(ex)) if power_on == False: # this should not fail as node was fenced succesfully logging.error('Timed out waiting to power ON\n') syslog.syslog(syslog.LOG_NOTICE, "Timed out waiting to power ON") print "Success: Rebooted" elif options["--action"] == "status": print "Status: " + status.upper() if status.upper() == "OFF": result = 2 elif options["--action"] == "monitor": pass except pexpect.EOF: fail(EC_CONNECTION_LOST) except pexpect.TIMEOUT: fail(EC_TIMED_OUT) except pycurl.error, ex: logging.error("%s\n" % str(ex)) syslog.syslog(syslog.LOG_ERR, ex[1]) fail(EC_TIMED_OUT) return result def fence_login(options, re_login_string = r"(login\s*: )|(Login Name: )|(username: )|(User Name :)"): force_ipvx = "" if (options.has_key("--inet6-only")): force_ipvx = "-6 " if (options.has_key("--inet4-only")): force_ipvx = "-4 " if (options.has_key("eol") == False): options["eol"] = "\r\n" if options.has_key("--command-prompt") and type(options["--command-prompt"]) is not list: options["--command-prompt"] = [ options["--command-prompt"] ] ## Do the delay of the fence device before logging in ## Delay is important for two-node clusters fencing but we do not need to delay 'status' operations if options["--action"] in ["off", "reboot"]: logging.info("Delay %s second(s) before logging in to the fence device" % options["--delay"]) time.sleep(int(options["--delay"])) try: re_login = re.compile(re_login_string, re.IGNORECASE) re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE) if options.has_key("--ssl"): gnutls_opts = "" if options.has_key("--notls"): gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:-VERS-TLS1.0:+VERS-SSL3.0\"" command = '%s %s --insecure --crlf -p %s %s' % \ (SSL_PATH, gnutls_opts, options["--ipport"], options["--ip"]) try: conn = fspawn(options, command) except pexpect.ExceptionPexpect, ex: logging.error("%s\n" % str(ex)) syslog.syslog(syslog.LOG_ERR, str(ex)) sys.exit(EC_GENERIC_ERROR) elif options.has_key("--ssh") and 0 == options.has_key("--identity-file"): command = '%s %s %s@%s -p %s -o PubkeyAuthentication=no' % \ (SSH_PATH, force_ipvx, options["--username"], options["--ip"], options["--ipport"]) if options.has_key("--ssh-options"): command += ' ' + options["--ssh-options"] conn = fspawn(options, command) if options.has_key("telnet_over_ssh"): # This is for stupid ssh servers (like ALOM) which behave more like telnet # (ignore name and display login prompt) result = conn.log_expect(options, \ [ re_login, "Are you sure you want to continue connecting (yes/no)?" ], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") # Host identity confirm conn.log_expect(options, re_login, int(options["--login-timeout"])) conn.sendline(options["--username"]) conn.log_expect(options, re_pass, int(options["--login-timeout"])) else: result = conn.log_expect(options, \ [ "ssword:", "Are you sure you want to continue connecting (yes/no)?" ], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") conn.log_expect(options, "ssword:", int(options["--login-timeout"])) conn.sendline(options["--password"]) conn.log_expect(options, options["--command-prompt"], int(options["--login-timeout"])) elif options.has_key("--ssh") and options.has_key("--identity-file"): command = '%s %s %s@%s -i %s -p %s' % \ (SSH_PATH, force_ipvx, options["--username"], options["--ip"], \ options["--identity-file"], options["--ipport"]) if options.has_key("--ssh-options"): command += ' ' + options["--ssh-options"] conn = fspawn(options, command) result = conn.log_expect(options, [ "Enter passphrase for key '" + options["--identity-file"] + "':", \ "Are you sure you want to continue connecting (yes/no)?" ] + \ options["--command-prompt"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") result = conn.log_expect(options, [ "Enter passphrase for key '" + options["--identity-file"]+"':"] + \ options["--command-prompt"], int(options["--login-timeout"])) if result == 0: if options.has_key("--password"): conn.sendline(options["--password"]) conn.log_expect(options, options["--command-prompt"], int(options["--login-timeout"])) else: fail_usage("Failed: You have to enter passphrase (-p) for identity file") else: conn = fspawn(options, TELNET_PATH) conn.send("set binary\n") conn.send("open %s -%s\n"%(options["--ip"], options["--ipport"])) result = conn.log_expect(options, re_login, int(options["--login-timeout"])) conn.send_eol(options["--username"]) ## automatically change end of line separator screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) if (re_login.search(screen) != None): options["eol"] = "\n" conn.send_eol(options["--username"]) result = conn.log_expect(options, re_pass, int(options["--login-timeout"])) elif (re_pass.search(screen) == None): conn.log_expect(options, re_pass, int(options["--shell-timeout"])) try: conn.send_eol(options["--password"]) valid_password = conn.log_expect(options, [ re_login ] + \ options["--command-prompt"], int(options["--shell-timeout"])) if valid_password == 0: ## password is invalid or we have to change EOL separator options["eol"] = "\r" conn.send_eol("") screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) ## after sending EOL the fence device can either show 'Login' or 'Password' if (re_login.search(screen) != None): conn.send_eol("") conn.send_eol(options["--username"]) conn.log_expect(options, re_pass, int(options["--login-timeout"])) conn.send_eol(options["--password"]) conn.log_expect(options, options["--command-prompt"], int(options["--login-timeout"])) except KeyError: fail(EC_PASSWORD_MISSING) except pexpect.EOF: fail(EC_LOGIN_DENIED) except pexpect.TIMEOUT: fail(EC_LOGIN_DENIED) return conn def is_executable(path): if os.path.exists(path): stats = os.stat(path) if stat.S_ISREG(stats.st_mode) and os.access(path, os.X_OK): return True return False diff --git a/fence/agents/rhevm/fence_rhevm.py b/fence/agents/rhevm/fence_rhevm.py index 8efdc456..767c0985 100644 --- a/fence/agents/rhevm/fence_rhevm.py +++ b/fence/agents/rhevm/fence_rhevm.py @@ -1,133 +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("") + conn.setopt(pycurl.POSTFIELDS, "") - c.setopt(pycurl.WRITEFUNCTION, b.write) - c.perform() - result = b.getvalue() + conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write) + conn.perform() + result = web_buffer.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/xenapi/fence_xenapi.py b/fence/agents/xenapi/fence_xenapi.py index 3b6454c5..2a52c984 100644 --- a/fence/agents/xenapi/fence_xenapi.py +++ b/fence/agents/xenapi/fence_xenapi.py @@ -1,228 +1,228 @@ #!/usr/bin/python -tt # ############################################################################# # Copyright 2011 Matthew Clark # This file is part of fence-xenserver # # fence-xenserver is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # fence-xenserver is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please let me know if you are using this script so that I can work out # whether I should continue support for it. mattjclark0407 at hotmail dot com ############################################################################# ############################################################################# # It's only just begun... # Current status: completely usable. This script is now working well and, # has a lot of functionality as a result of the fencing.py library and the # XenAPI libary. ############################################################################# # Please let me know if you are using this script so that I can work out # whether I should continue support for it. mattjclark0407 at hotmail dot com import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * import XenAPI #BEGIN_VERSION_GENERATION RELEASE_VERSION="" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION EC_BAD_SESSION = 1 # Find the status of the port given in the -U flag of options. def get_power_fn(session, options): if options.has_key("--verbose"): verbose = True else: verbose = False try: # Get a reference to the vm specified in the UUID or vm_name/port parameter vm = return_vm_reference(session, options) # Query the VM for its' associated parameters record = session.xenapi.VM.get_record(vm) # Check that we are not trying to manipulate a template or a control # domain as they show up as VM's with specific properties. if not(record["is_a_template"]) and not(record["is_control_domain"]): status = record["power_state"] if verbose: print "UUID:", record["uuid"], "NAME:", record["name_label"], "POWER STATUS:", record["power_state"] # Note that the VM can be in the following states (from the XenAPI document) # Halted: VM is offline and not using any resources. # Paused: All resources have been allocated but the VM itself is paused and its vCPUs are not running # Running: Running # Paused: VM state has been saved to disk and it is nolonger running. Note that disks remain in-Use while # We want to make sure that we only return the status "off" if the machine is actually halted as the status # is checked before a fencing action. Only when the machine is Halted is it not consuming resources which # may include whatever you are trying to protect with this fencing action. return (status=="Halted" and "off" or "on") except Exception, exn: print str(exn) return "Error" # Set the state of the port given in the -U flag of options. def set_power_fn(session, options): action = options["--action"].lower() try: # Get a reference to the vm specified in the UUID or vm_name/port parameter vm = return_vm_reference(session, options) # Query the VM for its' associated parameters record = session.xenapi.VM.get_record(vm) # Check that we are not trying to manipulate a template or a control # domain as they show up as VM's with specific properties. if not(record["is_a_template"]) and not(record["is_control_domain"]): if( action == "on" ): # Start the VM session.xenapi.VM.start(vm, False, True) elif( action == "off" ): # Force shutdown the VM session.xenapi.VM.hard_shutdown(vm) elif( action == "reboot" ): # Force reboot the VM session.xenapi.VM.hard_reboot(vm) except Exception, exn: print str(exn) # Function to populate an array of virtual machines and their status def get_outlet_list(session, options): result = {} if options.has_key("--verbose"): verbose = True else: verbose = False try: # Return an array of all the VM's on the host vms = session.xenapi.VM.get_all() for vm in vms: # Query the VM for its' associated parameters record = session.xenapi.VM.get_record(vm) # Check that we are not trying to manipulate a template or a control # domain as they show up as VM's with specific properties. if not(record["is_a_template"]) and not(record["is_control_domain"]): name = record["name_label"] uuid = record["uuid"] status = record["power_state"] result[uuid] = (name, status) if verbose: print "UUID:", record["uuid"], "NAME:", name, "POWER STATUS:", record["power_state"] except Exception, exn: print str(exn) return result # Function to initiate the XenServer session via the XenAPI library. def connect_and_login(options): url = options["--session-url"] username = options["--username"] password = options["--password"] try: # Create the XML RPC session to the specified URL. session = XenAPI.Session(url) # Login using the supplied credentials. session.xenapi.login_with_password(username, password) except Exception, exn: print str(exn) # http://sources.redhat.com/cluster/wiki/FenceAgentAPI says that for no connectivity # the exit value should be 1. It doesn't say anything about failed logins, so # until I hear otherwise it is best to keep this exit the same to make sure that # anything calling this script (that uses the same information in the web page # above) knows that this is an error condition, not a msg signifying a down port. sys.exit(EC_BAD_SESSION) return session # return a reference to the VM by either using the UUID or the vm_name/port. If the UUID is set then # this is tried first as this is the only properly unique identifier. # Exceptions are not handled in this function, code that calls this must be ready to handle them. def return_vm_reference(session, options): if options.has_key("--verbose"): verbose = True else: verbose = False # Case where the UUID has been specified if options.has_key("--uuid"): uuid = options["--uuid"].lower() # When using the -n parameter for name, we get an error message (in verbose # mode) that tells us that we didn't find a VM. To immitate that here we # need to catch and re-raise the exception produced by get_by_uuid. try: return session.xenapi.VM.get_by_uuid(uuid) except Exception: if verbose: print "No VM's found with a UUID of \"%s\"" % uuid raise # Case where the vm_name/port has been specified if options.has_key("--plug"): vm_name = options["--plug"] vm_arr = session.xenapi.VM.get_by_name_label(vm_name) # Need to make sure that we only have one result as the vm_name may # not be unique. Average case, so do it first. if len(vm_arr) == 1: return vm_arr[0] else: if len(vm_arr) == 0: if verbose: print "No VM's found with a name of \"%s\"" % vm_name # NAME_INVALID used as the XenAPI throws a UUID_INVALID if it can't find # a VM with the specified UUID. This should make the output look fairly # consistent. raise Exception("NAME_INVALID") else: if verbose: print "Multiple VM's have the name \"%s\", use UUID instead" % vm_name raise Exception("MULTIPLE_VMS_FOUND") # We should never get to this case as the input processing checks that either the UUID or # the name parameter is set. Regardless of whether or not a VM is found the above if # statements will return to the calling function (either by exception or by a reference # to the VM). raise Exception("VM_LOGIC_ERROR") def main(): device_opt = [ "login", "passwd", "port", "no_login", "no_password", "session_url" ] atexit.register(atexit_handler) options = check_input(device_opt, process_input(device_opt)) docs = { } docs["shortdesc"] = "XenAPI based fencing for the Citrix XenServer virtual machines." docs["longdesc"] = "\ fence_cxs is an I/O Fencing agent used on Citrix XenServer hosts. \ It uses the XenAPI, supplied by Citrix, to establish an XML-RPC sesssion \ to a XenServer host. Once the session is established, further XML-RPC \ commands are issued in order to switch on, switch off, restart and query \ the status of virtual machines running on the host." docs["vendorurl"] = "http://www.xenproject.org" show_docs(options, docs) - xenSession = connect_and_login(options) + xen_session = connect_and_login(options) # Operate the fencing device - result = fence_action(xenSession, options, set_power_fn, get_power_fn, get_outlet_list) + result = fence_action(xen_session, options, set_power_fn, get_power_fn, get_outlet_list) sys.exit(result) if __name__ == "__main__": main()