diff options
Diffstat (limited to 'agents/apc')
-rw-r--r-- | agents/apc/fence_apc.py | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/agents/apc/fence_apc.py b/agents/apc/fence_apc.py new file mode 100644 index 0000000..3ea0f37 --- /dev/null +++ b/agents/apc/fence_apc.py @@ -0,0 +1,263 @@ +#!@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, time +import logging +import atexit +sys.path.append("@FENCEAGENTSLIBDIR@") +from fencing import * +from fencing import fail, fail_usage, EC_STATUS + +# Fix for connection timed out issue in: +# https://bugzilla.redhat.com/show_bug.cgi?id=1342584 +TIMEDOUT_DELAY = 0.5 + +def get_power_status(conn, options): + exp_result = 0 + outlets = {} + + conn.send_eol("1") + conn.log_expect(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 "--switch" not in options: + fail_usage("Failed: You have to enter physical switch number") + else: + if "--switch" not in options: + 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["--command-prompt"], int(options["--shell-timeout"])) + conn.send_eol("1") + else: + conn.send_eol(options["--switch"]) + + while True: + exp_result = conn.log_expect( + ["Press <ENTER>"] + 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 line in lines: + res = show_re.search(line) + if res != None: + outlets[res.group(2)] = (res.group(3), res.group(4)) + time.sleep(TIMEDOUT_DELAY) + conn.send_eol("") + if exp_result != 0: + break + conn.send(chr(0o3)) + conn.log_expect("- Logout", int(options["--shell-timeout"])) + conn.log_expect(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 as e: + logging.error("Failed: {}".format(str(e))) + fail(EC_STATUS) + +def set_power_status(conn, options): + action = { + 'on' : "1", + 'off': "2" + }[options["--action"]] + + conn.send_eol("1") + conn.log_expect(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 "--switch" not in options: + fail_usage("Failed: You have to enter physical switch number") + else: + if "--switch" not in options: + 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["--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( + ["Press <ENTER>"] + options["--command-prompt"], int(options["--shell-timeout"])): + time.sleep(TIMEDOUT_DELAY) + conn.send_eol("") + + conn.send_eol(options["--plug"]+"") + conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) + + if switch == 0: + if admin2 == 1: + conn.send_eol("1") + conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) + if admin3 == 1: + conn.send_eol("1") + conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) + else: + conn.send_eol("1") + conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) + + conn.send_eol(action) + conn.log_expect("Enter 'YES' to continue or <ENTER> to cancel :", int(options["--shell-timeout"])) + conn.send_eol("YES") + conn.log_expect("Press <ENTER> to continue...", int(options["--power-timeout"])) + time.sleep(TIMEDOUT_DELAY) + conn.send_eol("") + conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) + conn.send(chr(0o3)) + conn.log_expect("- Logout", int(options["--shell-timeout"])) + conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) + +def get_power_status5(conn, options): + outlets = {} + + conn.send_eol("olStatus all") + + conn.log_expect(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 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 as e: + logging.error("Failed: {}".format(str(e))) + 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["--command-prompt"], int(options["--power-timeout"])) + +def main(): + device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \ + "port", "switch", "telnet"] + + 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 (("--plug" in options) == 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) in [ "5", "6", "7" ]): + 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) + + fence_logout(conn, "4") + sys.exit(result) + +if __name__ == "__main__": + main() |