From mboxrd@z Thu Jan 1 00:00:00 1970 From: jparsons@sourceware.org Date: 31 Jan 2007 20:47:21 -0000 Subject: [Cluster-devel] cluster/fence/agents/apc Makefile fence_apc.py Message-ID: <20070131204721.6028.qmail@sourceware.org> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit CVSROOT: /cvs/cluster Module name: cluster Changes by: jparsons at sourceware.org 2007-01-31 20:47:20 Modified files: fence/agents/apc: Makefile Added files: fence/agents/apc: fence_apc.py Log message: New apc agent written in python that supports named outlets and outlet groups, minus perl pain. Addresses bz172179 and bz134489. yee haw Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/apc/fence_apc.py.diff?cvsroot=cluster&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/fence/agents/apc/Makefile.diff?cvsroot=cluster&r1=1.2&r2=1.3 --- cluster/fence/agents/apc/fence_apc.py 2007/01/31 18:57:29 1.1 +++ cluster/fence/agents/apc/fence_apc.py 2007/01/31 20:47:19 1.2 @@ -0,0 +1,730 @@ +#!/usr/bin/python + +############################################################################### +############################################################################### +## +## Copyright (C) 2006 Red Hat, Inc. All rights reserved. +## +## This copyrighted material is made available to anyone wishing to use, +## modify, copy, or redistribute it subject to the terms and conditions +## of the GNU General Public License v.2. +## +############################################################################### +############################################################################### + +import getopt, sys +import os +import socket +import time + +from telnetlib import Telnet + +TELNET_TIMEOUT=5 #How long to wait for a response from a telnet try + +# WARNING!! Do not add code bewteen "#BEGIN_VERSION_GENERATION" and +# "#END_VERSION_GENERATION" It is generated by the Makefile + +#BEGIN_VERSION_GENERATION +FENCE_RELEASE_NAME="New APC Agent - test release" +REDHAT_COPYRIGHT="" +BUILD_DATE="September 21, 2006" +#END_VERSION_GENERATION + +POWER_OFF = 0 +POWER_ON = 1 +POWER_STATUS = 2 +POWER_REBOOT = 3 + +COMPLETE = 0 +NOT_COMPLETE = 1 + +ON = "ON" +OFF = "OFF" + +SUCCESS = "success" +FAIL = "fail" + +address = "" +login = "" +passwd = "" +port = "" +switchnum = "" +action = POWER_REBOOT #default action +verbose = False + +logfile = None + +#set up regex list +CONTROL_CONSOLE = "Control Console -----" +DEVICE_MANAGER = "Device Manager -----" +OUTLET_CONTROL = "- Outlet Control/Configuration -----" +CONTROL_OUTLET = "- Control Outlet -----" +CONTROL_OUTLET_2 = "- Outlet Control " +COMMAND_SUCCESS = "Command successfully issued." +COMMAND_SUCCESS_2 = " Success" +CONFIRM = "Enter 'YES' to continue or to cancel :" +CONTINUE = "Press to continue..." +SCREEN_END = "- Main Menu, - Refresh, - Event Log" +SCREEN_END_2 = "- Back, - Refresh, - Event Log" +USERNAME = "User Name :" +PASSWORD = "Password :" +MASTER = "------- MasterSwitch" + +CONTINUE_INDEX = 0 + +regex_list = list() +regex_list.append(CONTINUE) +regex_list.append(SCREEN_END) +regex_list.append(SCREEN_END_2) +regex_list.append(USERNAME) +regex_list.append(PASSWORD) + +def usage(): + print "Usage:\n" + print "fence_apc [options]" + print "Options:" + print " -a ip or hostname of APC switch" + print " -h print out help" + print " -l [login] login name" + print " -n [port] switch port" + print " -p [password] password" + print " -o [action] Reboot (default), Off, On, or Status" + print " -v Verbose Verbose mode - writes file to /tmp/apclog" + print " -V Print Version, then exit" + + sys.exit (0) + +def version(): + print "fence_apc %s %s\n" % (FENCE_RELEASE_NAME, BUILD_DATE) + print "%s\n" % REDHAT_COPYRIGHT + sys.exit(0) + +def main(): + + global address, login, passwd, port, action, verbose, logfile, switchnum + + if len(sys.argv) > 1: + try: + opts, args = getopt.getopt(sys.argv[1:], "a:hl:o:n:p:vV", ["help", "output="]) + except getopt.GetoptError: + #print help info and quit + usage() + sys.exit(2) + + for o, a in opts: + if o == "-v": + verbose = True + if o == "-V": + version() + if o in ("-h", "--help"): + usage() + sys.exit() + if o == "-l": + login = a + if o == "-p": + passwd = a + if o == "-n": + dex = a.find(":") + if dex == (-1): + port = a + else: + switchnum = a[:dex] + port = a[(dex+1):] + if o == "-o": + if a == "Off" or a == "OFF" or a == "off": + action = POWER_OFF + elif a == "On" or a == "ON" or a == "on": + action = POWER_ON + elif a == "Status" or a == "STATUS" or a == "status": + action = POWER_STATUS + elif a == "Reboot" or a == "REBOOT" or a == "reboot": + action = POWER_REBOOT + else: + usage() + sys.exit() + if o == "-a": + address = a + if address == "" or login == "" or passwd == "" or port == "": + usage() + sys.exit() + + else: #Take args from stdin... + params = {} + #place params in dict + for line in sys.stdin: + val = line.split("=") + params[val[0]] = val[1] + + try: + address = params["ipaddr"] + except KeyError, e: + sys.stderr.write("FENCE: Missing ipaddr param for fence_apc...exiting") + sys.exit(1) + try: + login = params["login"] + except KeyError, e: + sys.stderr.write("FENCE: Missing login param for fence_apc...exiting") + sys.exit(1) + try: + passwd = params["passwd"] + except KeyError, e: + sys.stderr.write("FENCE: Missing passwd param for fence_apc...exiting") + sys.exit(1) + try: + port = params["port"] + except KeyError, e: + sys.stderr.write("FENCE: Missing port param for fence_apc...exiting") + sys.exit(1) + try: + switchnum = params["switch"] + except KeyError, e: + pass + try: + verbose = params["verbose"] + verbose = (verbose == 'True' or verbose == 'true' or verbose == 'TRUE') + except KeyError, e: + pass + + try: + a = params["option"] + if a == "Off" or a == "OFF" or a == "off": + action = POWER_OFF + elif a == "On" or a == "ON" or a == "on": + action = POWER_ON + elif a == "Reboot" or a == "REBOOT" or a == "reboot": + action = POWER_REBOOT + except KeyError, e: + action = POWER_REBOOT + + #### End of stdin section + + ### Order of events + # 0) If verbose, prepare log file handle + # 1) Open socket + # 2) Log in + # 3) Evaluate task. Task will be one of: + # 3a - Check status and print to stdout (or log file if verbose) + # 3b - Turn a port off, then confirm + # 3c - Turn a port on, then confirm + # 3d - Reboot by turning a port off, then on, and confirming each step. + + if verbose: + setup_logging() + + sock = setup_socket() + + # Ok, now lets log in... + do_login(sock) + + # Now we should be at the outside Control screen + + if action == POWER_STATUS: + # We should be at the Control screen, so we need to write a '1' + # to kick things off + sock.write("1\r") + statusval = do_status_check(sock) + backout(sock) + sock.write("4\r") # Logs out + + elif action == POWER_OFF: + sock.write("1\r") + do_power_off(sock) + backout(sock) # Return to control screen + statusval = do_status_check(sock) + if statusval == OFF: + if verbose: + logit("Power Off successful\n") + print "Power Off successful" + backout(sock) + sock.write("4\r") # Logs out + sock.close() + sys.exit(0) + else: + if verbose: + logit("Power Off unsuccessful\n") + logit("Undetermined error\n") + sys.stderr.write("Power Off unsuccessful") + backout(sock) + sock.write("4\r") # Logs out + sock.close() + sys.exit(1) + + elif action == POWER_ON: + sock.write("1\r") + do_power_on(sock) + backout(sock) # Return to control screen + statusval = do_status_check(sock) + if statusval == ON: + if verbose: + logit("Power On successful\n") + print "Power On successful" + backout(sock) + sock.write("4\r") # logs out + sock.close() + sys.exit(0) + else: + if verbose: + logit("Power On unsuccessful\n") + logit("Undetermined error\n") + sys.stderr.write("Power On unsuccessful") + backout(sock) + sock.write("4\r") # Logs out + sock.close() + sys.exit(1) + + elif action == POWER_REBOOT: + sock.write("1\r") + do_power_off(sock) + backout(sock) # Return to control screen + statusval = do_status_check(sock) + if statusval == OFF: + if verbose: + logit("Power Off successful\n") + print "Power Off successful" + backout(sock) + else: + if verbose: + logit("Power Off unsuccessful\n") + logit("Undetermined error\n") + sys.stderr.write("Power Off unsuccessful") + backout(sock) + sock.write("4\r") # Logs out + sock.close() + sys.exit(1) + do_power_on(sock) + backout(sock) # Return to control screen + statusval = do_status_check(sock) + if statusval == ON: + if verbose: + logit("Power Reboot successful\n") + print "Power Reboot successful" + backout(sock) + sock.write("4\r") # Logs out + sock.close() + sys.exit(0) + else: + if verbose: + logit("Power Reboot unsuccessful\n") + logit("Undetermined error\n") + sys.stderr.write("Power Reboot unsuccessful") + backout(sock) + sock.write("4\r") # Logs out + sock.close() + sys.exit(1) + + sock.close() + +def backout(sock): + sock.write(chr(27)) + + while (1): + i, mo, txt = sock.expect(regex_list, TELNET_TIMEOUT) + + if regex_list[i] == SCREEN_END: + break + elif regex_list[i] == SCREEN_END_2: + sock.write(chr(27)) + +def setup_socket(): + ## Time to open telnet session and log in. + try: + sock = Telnet(address.strip()) + except socket.error, (errno, msg): + my_msg = "FENCE: A problem was encountered opening a telnet session with " + address + if verbose: + logit(my_msg) + logit("FENCE: Error number: %d -- Message: %s\n" % (errno, msg)) + logit("Firewall issue? Correct address?\n") + + sys.stderr.write(my_msg) + sys.stderr.write(("FENCE: Error number: %d -- Message: %s\n" % (errno, msg))) + sys.stderr.write("Firewall issue? Correct address?\n") + sys.exit(1) + + if verbose: + logit("\nsocket open to %s\n" % address) + + return sock + +def setup_logging( log_location="/tmp/apclog"): + global logfile + try: + logfile = open(log_location, 'a') + logfile.write("###############################################\n") + logfile.write("Telnetting to apc switch %s\n" % address) + now = time.localtime(time.time()) + logfile.write(time.asctime(now)) + except IOError, e: + sys.stderr.write("Failed to open log file %s" % log_location) + logfile = None + +def logit(instr): + if logfile != None: + logfile.write(instr) + +def do_login(sock): + result_code = 1 + + ## This loop tries to assemble complete telnet screens and passes + ## them to helper methods to handle responses accordingly. + while result_code: + try: + i, mo, txt = sock.expect(regex_list, TELNET_TIMEOUT) + except socket.error, (errno, msg): + my_msg = "FENCE: A problem was encountered opening a telnet session with " + address + "\n" + if verbose: + logit(my_msg) + logit("FENCE: Error number: %d -- Message: %s\n" % (errno, msg)) + + sys.stderr.write(my_msg) + sys.stderr.write(("FENCE: Error number: %d -- Message: %s\n" % (errno, msg))) + sys.exit(1) + + if i == CONTINUE_INDEX: # Capture the rest of the screen... + sock.write("\r") + ii,moo,txtt = sock.expect(regex_list, TELNET_TIMEOUT) + txt = txt + txtt + + ndbuf = sock.read_eager() # Scoop up remainder + if verbose: + logit(txt + ndbuf) + result_code,response = log_in(txt + ndbuf) + if result_code: + try: + sock.write(response) + except socket.error, (errno, msg): + if verbose: + logit("Error #%s" % errno) + logit(msg) + sys.stderr.write("Error #%s: %s" % (errno,msg)) + sys.exit(1) + +def log_in(buffer): + lines = buffer.splitlines() + + for i in lines: + if i.find(USERNAME) != (-1): + if verbose: + logit("Sending login: %s\n" % login) + return (NOT_COMPLETE, login + "\r") + elif i.find(PASSWORD) != (-1): + if verbose: + logit("Sending password: %s\n" % passwd) + return (NOT_COMPLETE, passwd + "\r") + elif i.find(CONTROL_CONSOLE) != (-1): + return (COMPLETE, "1\r") + +def do_status_check(sock): + result_code = 1 + while result_code: + i, mo, txt = sock.expect(regex_list, TELNET_TIMEOUT) + if i == CONTINUE_INDEX: # Capture the rest of the screen... + sock.write("\r") + ii,moo,txtt = sock.expect(regex_list, TELNET_TIMEOUT) + txt = txt + txtt + + ndbuf = sock.read_eager() # Scoop up remainder + if verbose: + logit(txt + ndbuf) + (result_code,response,statusval) = return_status(txt + ndbuf) + if result_code: + try: + sock.write(response) + except socket.error, (errno, msg): + if verbose: + logit("Status check failed.") + logit("Error #%s" % errno) + logit(msg) + sys.stderr.write("Status check failed.") + sys.stderr.write("Error #%s: %s" % (errno,msg)) + sys.exit(1) + # Back from status check - value should be in status var + if response == SUCCESS: + if switchnum == "": + if verbose: + logit("Status check successful. Port %s is %s" % (port,statusval)) + print "Status check successful. Port %s is %s" % (port,statusval) + else: + if verbose: + logit("Status check successful. Port %s:%s is %s" % (switchnum, port, statusval)) + print "Status check successful. Port %s:%s is %s" % (switchnum, port, statusval) + + return statusval + else: + if verbose: + logit("Status check failed, unknown reason.") + sys.stderr.write("Status check failed, unknown reason.\n") + sock.close() + sys.exit(1) + +def return_status(buffer): + global switchnum, port + + lines = buffer.splitlines() + + for i in lines: + if i.find(CONTROL_CONSOLE) != (-1): + return (NOT_COMPLETE, "1\r", "Status Unknown") + elif i.find(DEVICE_MANAGER) != (-1): + if switchnum != "": + res = switchnum + "\r" + else: + res = "3\r" + return (NOT_COMPLETE, res, "Status Unknown") + elif i.find(OUTLET_CONTROL) != (-1): + ls = buffer.splitlines() + portval = port.strip() + portval = " " + portval + " " + portval2 = " " + port.strip() + "- " + found_portval = False + for l in ls: + if l.find(portval) != (-1) or l.find(portval2) != (-1): + found_portval = True + linesplit = l.split() + linelen = len(linesplit) + return (COMPLETE,SUCCESS,linesplit[linelen - 1]) + elif i.find(MASTER) != (-1): + try: + e = int(port.strip()) + portval = port.strip() + switchval = switchnum.strip() + portval = switchval + ":" + portval + except ValueError, e: + portval = port.strip() + ls = buffer.splitlines() + found_portval = False + for l in ls: + words = l.split() + if len(words) > 3: + if words[2] == portval or words[3] == portval: + found_portval = True + linesplit = l.split() + linelen = len(linesplit) + return (COMPLETE, SUCCESS, linesplit[linelen - 3]) + return (COMPLETE, FAIL, "Incorrect port number") + return (NOT_COMPLETE, chr(27), "Status Unknown") + +def do_power_switch(sock, status): + result_code = 1 + + while result_code: + i, mo, txt = sock.expect(regex_list, TELNET_TIMEOUT) + if i == CONTINUE_INDEX: # Capture the rest of the screen... + sock.write("\r") + ii,moo,txtt = sock.expect(regex_list, TELNET_TIMEOUT) + txt = txt + txtt + + ndbuf = sock.read_eager() # Scoop up remainder + if verbose: + logit(txt + ndbuf) + + if status == "off": + result_code, response = power_off(txt + ndbuf) + elif status == "on": + result_code, response = power_on(txt + ndbuf) + else: + if verbose: + logit("Invalid status in do_power_switch() function") + sys.stderr.write("Invalid status in do_power_switch() function") + sys.exit(1) + + if result_code: + try: + sock.write(response) + except socket.error, (errno, msg): + if verbose: + logit("Error #%s" % errno) + logit(msg) + sys.stderr.write("Error #%s: %s" % (errno,msg)) + sys.exit(1) + # FIXME: always returns COMPLETE (0) + else: + try: + sock.write(response) + except socket.error, (errno, msg): + if verbose: + logit("Error #%s" % errno) + logit(msg) + sys.stderr.write("Error #%s: %s" % (errno,msg)) + sys.exit(1) + return COMPLETE + + +def power_switch(buffer, escape, control_outlet, control_outlet2): + # If port is not aliased, then outlet control screen will have the word + # 'Outlet' in the header. If the name is aliased, it will only have the + # alias in the header. + + outlet_search_str1 = "Outlet " + port.strip() + " ------------" + outlet_search_str2 = port.strip() + " ------------" + outlet_search_str3 = "Outlet " + switchnum.strip() + ":" + port.strip() + " ------" + outlet_search_str4 = " Outlet : " + port.strip() + outlet_search_str5 = " Outlet Name : " + port.strip() + master_search_str1 = "-------- Master" + lines = buffer.splitlines() + + for i in lines: + if i.find(CONTROL_CONSOLE) != (-1): + return (NOT_COMPLETE,"1\r") + + elif i.find(DEVICE_MANAGER) != (-1): + if switchnum != "": + res = switchnum + "\r" + else: + res = "3\r" + return (NOT_COMPLETE, res) + + elif (i.find(master_search_str1) != (-1)): + return (NOT_COMPLETE, port.strip() + "\r") + + elif i.find(outlet_search_str1) != (-1) and (switchnum == ""): + return (NOT_COMPLETE,"1\r") + + elif i.find(outlet_search_str2) != (-1) and (switchnum == ""): + return (NOT_COMPLETE,"1\r") + + elif i.find(outlet_search_str3) != (-1): + return (NOT_COMPLETE, "1\r") + + elif i == outlet_search_str4: + return (NOT_COMPLETE, "1\r") + + elif i == outlet_search_str5: + return (NOT_COMPLETE, "1\r") + + elif i.find(OUTLET_CONTROL) != (-1): + ls = buffer.splitlines() + portval = port.strip() + portval = " " + portval + " " + found_portval = False + i = 0 + # look for aliased name + for l in ls: + i = i + 1 + if l.find(portval) != (-1): + found_portval = True + linesplit = l.split() + outlet_str = linesplit[0] + dex = outlet_str.find("-") + if dex <= (0): + if verbose: + logit("Problem identifying outlet\n") + logit("Looking for %s in string %s\n" % (portval,outlet_str)) + sys.stderr.write("Problem identifying outlet\n") + sys.stderr.write("Looking for %s in string %s\n" % (portval,outlet_str)) + sys.exit(1) + normalized_outlet_str = outlet_str[:dex] + return (NOT_COMPLETE, normalized_outlet_str + "\r") + # look for portnum + portval = " " + port.strip() + "-" + i = 0 + for l in ls: + i = i + 1 + if l.find(portval) != (-1): + found_portval = True + linesplit = l.split() + outlet_str = linesplit[0] + dex = outlet_str.find("-") + if dex <= (0): + if verbose: + logit("Problem identifying outlet\n") + logit("Looking for %s in string %s\n" % (portval,outlet_str)) + sys.stderr.write("Problem identifying outlet\n") + sys.stderr.write("Looking for %s in string %s\n" % (portval,outlet_str)) + sys.exit(1) + normalized_outlet_str = outlet_str[:dex] + return (NOT_COMPLETE, normalized_outlet_str + "\r") + if found_portval == False: + if verbose: + logit("Problem identifying outlet\n") + logit("Looking for '%s' in string '%s'\n" % (portval, ls)) + sys.stderr.write("Problem identifying outlet\n") + sys.stderr.write("Looking for '%s' in string '%s'\n" % (portval, ls)) + sys.exit(1) + + elif i.find(MASTER) != (-1): + ls = buffer.splitlines() + found_portval = False + # look for aliased name + portval = port.strip() + for l in ls: + words = l.strip().split() + if len(words) > 3: + if words[3].strip() == portval: + outlet_str = words[0] + dex = outlet_str.find("-") + if dex <= (0): + if verbose: + logit("Problem identifying outlet\n") + logit("Looking for %s in string %s\n" % (portval, outlet_str)) + sys.stderr.write("Problem identifying outlet\n") + sys.stderr.write("Looking for %s in string %s\n" % (portval, outlet_str)) + sys.exit(1) + normalized_outlet_str = outlet_str[:dex] + return (NOT_COMPLETE, (normalized_outlet_str + "\r")) + # look for portnum + portval = port.strip() + portval = switchnum.strip() + ":" + portval + " " + i = 0 + for l in ls: + i = i + 1 + if l.find(portval) != (-1): + found_portval = True + linesplit = l.split() + outlet_str = linesplit[0] + dex = outlet_str.find("-") + if dex <= (0): + if verbose: + logit("Problem identifying outlet\n") + logit("Looking for %s in string %s\n" % (portval,outlet_str)) + sys.stderr.write("Problem identifying outlet\n") + sys.stderr.write("Looking for %s in string %s\n" % (portval,outlet_str)) + sys.exit(1) + normalized_outlet_str = outlet_str[:dex] + return (NOT_COMPLETE, (normalized_outlet_str + "\r")) + if found_portval == False: + if verbose: + logit("Problem identifying outlet\n") + logit("Looking for '%s' in string '%s'\n" % (portval, ls)) + sys.stderr.write("Problem identifying outlet\n") + sys.stderr.write("Looking for '%s' in string '%s'\n" % (portval, ls)) + sys.exit(1) + + elif i.find(CONFIRM) != (-1): + return (NOT_COMPLETE,"YES\r") + + elif i.find(COMMAND_SUCCESS) != (-1): + return (COMPLETE,"\r") + + elif i.find(COMMAND_SUCCESS_2) != (-1): + return (COMPLETE,"\r") + + elif i.find(CONTROL_OUTLET) != (-1): + return (NOT_COMPLETE, control_outlet + "\r") + + elif i.find(CONTROL_OUTLET_2) != (-1): + return (NOT_COMPLETE, control_outlet2 + "\r") + + if (escape == True): + return (NOT_COMPLETE, chr(27)) + else: + raise "unknown screen encountered in \n" + str(lines) + "\n" + +def do_power_off(sock): + x = do_power_switch(sock, "off") + return x + +def power_off(buffer): + x = power_switch(buffer, False, "2", "3"); + return x + +def do_power_on(sock): + x = do_power_switch(sock, "on") + return x + +def power_on(buffer): + x = power_switch(buffer, True, "1", "1"); + return x + +if __name__ == "__main__": + main() --- cluster/fence/agents/apc/Makefile 2006/08/11 15:18:07 1.2 +++ cluster/fence/agents/apc/Makefile 2007/01/31 20:47:19 1.3 @@ -11,7 +11,7 @@ ############################################################################### ############################################################################### -SOURCE= fence_apc.pl +SOURCE= fence_apc.py TARGET= fence_apc top_srcdir=../.. @@ -19,12 +19,12 @@ all: $(TARGET) -fence_apc: fence_apc.pl +fence_apc: fence_apc.py : > $(TARGET) awk "{print}(\$$1 ~ /#BEGIN_VERSION_GENERATION/){exit 0}" $(SOURCE) >> $(TARGET) - echo "\$$FENCE_RELEASE_NAME=\"${RELEASE}\";" >> $(TARGET) - ${top_srcdir}/scripts/define2var ${top_srcdir}/config/copyright.cf perl REDHAT_COPYRIGHT >> $(TARGET) - echo "\$$BUILD_DATE=\"(built `date`)\";" >> $(TARGET) + echo "FENCE_RELEASE_NAME=\"${RELEASE}\";" >> $(TARGET) + ${top_srcdir}/scripts/define2var ${top_srcdir}/config/copyright.cf sh REDHAT_COPYRIGHT >> $(TARGET) + echo "BUILD_DATE=\"(built `date`)\";" >> $(TARGET) awk -v p=0 "(\$$1 ~ /#END_VERSION_GENERATION/){p = 1} {if(p==1)print}" $(SOURCE) >> $(TARGET) chmod +x $(TARGET)