#!/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 = "" passwd_script = "" 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 -----" OUTLET_MANAGE = "- Outlet Management -----" 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" FIRMWARE_STR = "Rack PDU APP" CONTINUE_INDEX = 0 FIRMWARE_REV = 2 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 " -S [path] script to run to retrieve 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, passwd_script, port, action, verbose, logfile, switchnum if len(sys.argv) > 1: try: opts, args = getopt.getopt(sys.argv[1:], "a:hl:o:n:p:S: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 == "-S": passwd_script = 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 == "" and passwd_script == "") or port == "": usage() sys.exit() else: #Take args from stdin... params = {} #place params in dict for line in sys.stdin: val = line.split("=") if len(val) == 2: params[val[0].strip()] = val[1].strip() 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: if 'passwd' in params: passwd = params["passwd"] if 'passwd_script' in params: passwd_script = params['passwd_script'] if passwd == "" and passwd_script == "": raise "missing password" except: sys.stderr.write("FENCE: Missing passwd 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 # retrieve passwd from passwd_script (if specified) passwd_scr = '' if len(passwd_script): try: if not os.access(passwd_script, os.X_OK): raise 'script not executable' p = os.popen(passwd_script, 'r', 1024) passwd_scr = p.readline().strip() if p.close() != None: raise 'script failed' except: sys.stderr.write('password-script "%s" failed\n' % passwd_script) passwd_scr = '' if passwd == "" and passwd_scr == "": sys.stderr.write('password not available, exiting...') sys.exit(1) elif passwd == passwd_scr: pass elif passwd and passwd_scr: # execute self, with password_scr as passwd, # if that fails, continue with "passwd" argument as password if len(sys.argv) > 1: comm = sys.argv[0] skip_next = False for w in sys.argv[1:]: if skip_next: skip_next = False elif w in ['-p', '-S']: skip_next = True else: comm += ' ' + w comm += ' -p ' + passwd_scr ret = os.system(comm) if ret != -1 and os.WIFEXITED(ret) and os.WEXITSTATUS(ret) == 0: # success sys.exit(0) else: sys.stderr.write('Use of password from "passwd_script" failed, trying "passwd" argument\n') else: # use stdin p = os.popen(sys.argv[0], 'w', 1024) for par in params: if par not in ['passwd', 'passwd_script']: p.write(par + '=' + params[par] + '\n') p.write('passwd=' + passwd_scr + '\n') p.flush() if p.close() == None: # success sys.exit(0) else: sys.stderr.write('Use of password from "passwd_script" failed, trying "passwd" argument\n') elif passwd_scr: passwd = passwd_scr # passwd all set ### 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): global FIRMWARE_REV 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): #while we are here, grab the firmware revision rev_search_lines = buffer.splitlines() for rev_search_line in rev_search_lines: #search screen again rev_dex = rev_search_line.find(FIRMWARE_STR) if rev_dex != (-1): #found revision line scratch_rev = rev_search_line[rev_dex:] v_dex = scratch_rev.find("v") if v_dex != (-1): if scratch_rev[v_dex + 1] == "3": #format is v3.3.4 FIRMWARE_REV = 3 break 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: if FIRMWARE_REV == 2: res = "3\r" elif FIRMWARE_REV == 3: res = "2\r1\r" else: #placeholder for future revisions 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: if FIRMWARE_REV == 2: res = "3\r" elif FIRMWARE_REV == 3: #Changed for bz299191 #res = "2\r1\r" res = "2\r" else: #placeholder for future revisions - sheesh 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_MANAGE) != (-1): #Changed for bz299191 #return (NOT_COMPLETE, "\r") return (NOT_COMPLETE, "1\r") #elif i.find(OUTLET_CONTROL) != (-1) or i.find(OUTLET_MANAGE) != (-1): 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 '----' not in words[0] and 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()