From 9655fab4df8855f5d87af6f211333f599abe78c5 Mon Sep 17 00:00:00 2001 From: Stephen Soltesz Date: Tue, 6 Jan 2009 19:29:15 +0000 Subject: [PATCH] trimmed-down reboot module, now imports all pcucontrol models at once --- pcucontrol/reboot.py | 1377 ++++-------------------------------------- 1 file changed, 116 insertions(+), 1261 deletions(-) diff --git a/pcucontrol/reboot.py b/pcucontrol/reboot.py index decaf1d..3ad6438 100755 --- a/pcucontrol/reboot.py +++ b/pcucontrol/reboot.py @@ -19,6 +19,7 @@ import pcucontrol.transports.ssh.pexpect as pexpect import socket from monitor.util import command + # Use our versions of telnetlib and pyssh sys.path.insert(0, os.path.dirname(sys.argv[0])) import pcucontrol.transports.telnetlib as telnetlib @@ -28,9 +29,6 @@ from monitor import config from monitor.database.info.model import FindbadPCURecord -# Timeouts in seconds -TELNET_TIMEOUT = 45 - # Event class ID from pcu events #NODE_POWER_CONTROL = 3 @@ -52,16 +50,6 @@ class ExceptionReset(Exception): pass class ExceptionPort(Exception): pass class ExceptionUsername(Exception): pass -def telnet_answer(telnet, expected, buffer): - global verbose - - output = telnet.read_until(expected, TELNET_TIMEOUT) - #if verbose: - # logger.debug(output) - if output.find(expected) == -1: - raise ExceptionNotFound, "'%s' not found" % expected - else: - telnet.write(buffer + "\r\n") # PCU has model, host, preferred-port, user, passwd, @@ -111,19 +99,30 @@ class PCURecord: self.__setattr__("reboot_str", pcu_record_dict[field]) else: self.__setattr__(field, pcu_record_dict[field]) - else: - raise Exception("No such field %s in pcu record dict" % field) + #else: + # raise Exception("No such field %s in pcu record dict" % field) class Transport: - TELNET = 1 - SSH = 2 - HTTP = 3 - HTTPS = 4 - IPAL = 5 - DRAC = 6 + TELNET = "telnet" + SSH = "ssh" + HTTP = "http" + HTTPS = "https" + IPAL = "ipal" + DRAC = "drac" + AMT = "amt" TELNET_TIMEOUT = 120 + porttypemap = { + 5869 : DRAC, + 22 : SSH, + 23 : TELNET, + 443 : HTTPS, + 80 : HTTP, + 9100 : IPAL, + 16992 : AMT, + } + def __init__(self, type, verbose): self.type = type self.verbose = verbose @@ -137,7 +136,7 @@ class Transport: transport.set_debuglevel(self.verbose) if username is not None: self.transport = transport - self.ifThenSend(prompt, username, ExceptionUsername) + self.transport.ifThenSend(prompt, username, ExceptionUsername) elif self.type == self.SSH: if username is not None: @@ -148,6 +147,7 @@ class Transport: else: raise Exception("Username cannot be None for ssh transport.") elif self.type == self.HTTP: + # NOTE: this does not work for all web-based services... self.url = "http://%s:%d/" % (host,80) uri = "%s:%d" % (host,80) @@ -157,7 +157,6 @@ class Transport: authhandler = urllib2.HTTPBasicAuthHandler( authinfo ) transport = urllib2.build_opener(authhandler) - else: raise Exception("Unknown transport type: %s" % self.type) @@ -175,6 +174,28 @@ class Transport: raise Exception("Unknown transport type %s" % self.type) self.transport = None + def write(self, msg): + return self.send(msg) + + def send(self, msg): + if self.transport == None: + raise ExceptionNoTransport("transport object is type None") + + return self.transport.write(msg) + + def sendPassword(self, password, prompt=None): + if self.type == self.TELNET: + if prompt == None: + self.ifThenSend("Password", password, ExceptionPassword) + else: + self.ifThenSend(prompt, password, ExceptionPassword) + elif self.type == self.SSH: + self.ifThenSend("password:", password, ExceptionPassword) + elif self.type == self.HTTP: + pass + else: + raise Exception("Unknown transport type: %s" % self.type) + def sendHTTP(self, resource, data): if self.verbose: print "POSTing '%s' to %s" % (data,self.url + resource) @@ -191,19 +212,6 @@ class Transport: return 0 - def sendPassword(self, password, prompt=None): - if self.type == self.TELNET: - if prompt == None: - self.ifThenSend("Password", password, ExceptionPassword) - else: - self.ifThenSend(prompt, password, ExceptionPassword) - elif self.type == self.SSH: - self.ifThenSend("password:", password, ExceptionPassword) - elif self.type == self.HTTP: - pass - else: - raise Exception("Unknown transport type: %s" % self.type) - def ifThenSend(self, expected, buffer, ErrorClass=ExceptionPrompt): if self.transport != None: @@ -221,46 +229,71 @@ class Transport: self.transport.read_until(expected, self.TELNET_TIMEOUT) except: raise ErrorClass("Could not find '%s' within timeout" % expected) - -class PCUControl(Transport,PCUModel,PCURecord): +class PCUControl(PCUModel,PCURecord): + + """ + There are three cases: + 1) the pcu_record passed below includes port_status from an + external probe. + 2) the external probe failed, and the values are empty + 3) this call is made independent of port_status. + + In the first case, the first open port is used. + In the third case, the ports are tried in sequence. + + In this way, the port_status value serves only as an optimization, + because closed ports are avoided. The supported_ports value should + order ports by their preferred usage. + """ supported_ports = [] - def __init__(self, plc_pcu_record, verbose, supported_ports=[]): + def __init__(self, plc_pcu_record, verbose, ignored=None): PCUModel.__init__(self, plc_pcu_record) PCURecord.__init__(self, plc_pcu_record) - type = None - if self.port_status: - # NOTE: prefer racadm port over ssh - if '5869' in supported_ports and self.port_status['5869'] == "open": - type = Transport.DRAC# DRAC cards user this port. - elif '22' in supported_ports and self.port_status['22'] == "open": - type = Transport.SSH - elif '23' in supported_ports and self.port_status['23'] == "open": - type = Transport.TELNET - # NOTE: prefer https over http - elif '443' in supported_ports and self.port_status['443'] == "open": - type = Transport.HTTPS - elif '80' in supported_ports and self.port_status['80'] == "open": - type = Transport.HTTP - elif '9100' in supported_ports and self.port_status['9100'] == "open": - type = Transport.IPAL - elif '16992' in supported_ports and self.port_status['16992'] == "open": - type = Transport.HTTP - else: + + def reboot(self, node_port, dryrun): + + port_list = [] + if hasattr(self, 'port_status') and self.port_status: + port_list = filter(lambda x: self.port_status[x] == "open" , self.port_status.keys()) + port_list = [ int(x) for x in port_list ] + if port_list == []: raise ExceptionPort("Unsupported Port: No transport from open ports") else: - raise ExceptionPort("No Portstatus: No transport because no open ports") - Transport.__init__(self, type, verbose) + port_list = self.supported_ports + + print port_list + + ret = "could not run" + for port in port_list: + if port not in Transport.porttypemap: + continue + + type = Transport.porttypemap[port] + self.transport = Transport(type, verbose) + + if hasattr(self, "run_%s" % type): + fxn = getattr(self, "run_%s" % type) + ret = self.catcherror(fxn, node_port, dryrun) + if ret == 0: # NOTE: success!, so stop + break + else: + continue + + return ret def run(self, node_port, dryrun): """ This function is to be defined by the specific PCU instance. """ + raise Exception("This function is not implemented") pass - - def reboot(self, node_port, dryrun): + + #def reboot(self, node_port, dryrun): + + def catcherror(self, function, node_port, dryrun): try: - return self.run(node_port, dryrun) + return function(node_port, dryrun) except ExceptionNotFound, err: return "error: " + str(err) except ExceptionPassword, err: @@ -292,1056 +325,7 @@ class PCUControl(Transport,PCUModel,PCURecord): traceback.print_exc() return "EOF connection reset" + str(err) -class IPMI(PCUControl): - - supported_ports = [80,443,623] - - # TODO: get exit codes to determine success or failure... - def run(self, node_port, dryrun): - - if not dryrun: - cmd = "ipmitool -I lanplus -H %s -U %s -P '%s' power cycle" - p = os.popen(cmd % ( self.host, self.username, self.password) ) - result = p.read() - print "RESULT: ", result - else: - cmd = "ipmitool -I lanplus -H %s -U %s -P '%s' user list" - p = os.popen(cmd % ( self.host, self.username, self.password) ) - result = p.read() - print "RESULT: ", result - - if "Error" in result: - return result - else: - return 0 - -class IPAL(PCUControl): - """ - This now uses a proprietary format for communicating with the PCU. I - prefer it to Telnet, and Web access, since it's much lighter weight - and, more importantly, IT WORKS!! HHAHHHAHAHAHAHAHA! - """ - supported_ports = [23,80,9100] - - def format_msg(self, data, cmd): - esc = chr(int('1b',16)) - return "%c%s%c%s%c" % (esc, self.password, esc, data, cmd) # esc, 'q', chr(4)) - - def recv_noblock(self, s, count): - import errno - - try: - # TODO: make sleep backoff, before stopping. - time.sleep(4) - ret = s.recv(count, socket.MSG_DONTWAIT) - except socket.error, e: - if e[0] == errno.EAGAIN: - raise Exception(e[1]) - else: - # TODO: not other exceptions. - raise Exception(e) - return ret - - def run(self, node_port, dryrun): - if self.type == Transport.IPAL: - ret = self.run_ipal(node_port, dryrun) - if ret != 0: - ret2 = self.run_telnet(node_port, dryrun) - if ret2 != 0: - return ret - return ret2 - return ret - elif self.type == Transport.TELNET: - return self.run_telnet(node_port, dryrun) - else: - raise ExceptionNoTransport("Unimplemented Transport for IPAL") - - def run_telnet(self, node_port, dryrun): - # TELNET version of protocol... - self.open(self.host) - ## XXX Some iPals require you to hit Enter a few times first - self.ifThenSend("Password >", "\r\n\r\n", ExceptionNotFound) - # Login - self.ifThenSend("Password >", self.password, ExceptionPassword) - self.transport.write("\r\n\r\n") - if not dryrun: # P# - Pulse relay - print "node_port %s" % node_port - self.ifThenSend("Enter >", - "P7", # % node_port, - ExceptionNotFound) - print "send newlines" - self.transport.write("\r\n\r\n") - print "after new lines" - # Get the next prompt - print "wait for enter" - self.ifElse("Enter >", ExceptionTimeout) - print "closing " - self.close() - return 0 - - def run_ipal(self, node_port, dryrun): - import errno - - power_on = False - - print "open socket" - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - print "connect" - s.connect((self.host, 9100)) - except socket.error, e: - s.close() - if e[0] == errno.ECONNREFUSED: - # cannot connect to remote host - raise Exception(e[1]) - else: - # TODO: what other conditions are there? - raise Exception(e) - - # get current status - print "Checking status" - s.send(self.format_msg("", 'O')) - ret = self.recv_noblock(s, 8) - print "Current status is '%s'" % ret - - if ret == '': - raise Exception("Status returned 'another session already open' %s : %s" % (node_port, ret)) - - if node_port < len(ret): - status = ret[node_port] - if status == '1': - # up - power_on = True - elif status == '0': - # down - power_on = False - else: - raise Exception("Unknown status for PCU socket %s : %s" % (node_port, ret)) - else: - raise Exception("Mismatch between configured port and PCU status: %s %s" % (node_port, ret)) - - - if not dryrun: - if power_on: - print "Pulsing %s" % node_port - s.send(self.format_msg("%s" % node_port, 'P')) - else: - # NOTE: turn power on ; do not pulse the port. - print "Power was off, so turning on ..." - s.send(self.format_msg("%s" % node_port, 'E')) - #s.send(self.format_msg("%s" % node_port, 'P')) - - print "Receiving response." - ret = self.recv_noblock(s, 8) - print "Current status is '%s'" % ret - - if node_port < len(ret): - status = ret[node_port] - if status == '1': - # up - power_on = True - elif status == '0': - # down - power_on = False - else: - raise Exception("Unknown status for PCU socket %s : %s" % (node_port, ret)) - else: - raise Exception("Mismatch between configured port and PCU status: %s %s" % (node_port, ret)) - - if power_on: - return 0 - else: - return "Failed Power On" - - s.close() - return 0 - -class APCControl(PCUControl): - supported_ports = [22,23,80,443] - reboot_sequence = [] - - def run(self, node_port, dryrun): - print "RUNNING!!!!!!!!!!!!" - if self.type == Transport.HTTPS or self.type == Transport.HTTP: - print "APC via http...." - return self.run_http_or_https(node_port, dryrun) - else: - print "APC via telnet/ssh...." - return self.run_telnet_or_ssh(node_port, dryrun) - - def run_telnet_or_ssh(self, node_port, dryrun): - self.open(self.host, self.username) - self.sendPassword(self.password) - - first = True - for val in self.reboot_sequence: - if first: - self.ifThenSend("\r\n> ", val, ExceptionPassword) - first = False - else: - self.ifThenSend("\r\n> ", val) - - if not dryrun: - self.ifThenSend("Enter 'YES' to continue or to cancel", - "YES\r\n", - ExceptionSequence) - else: - self.ifThenSend("Enter 'YES' to continue or to cancel", - "", ExceptionSequence) - self.ifThenSend("Press to continue...", "", ExceptionSequence) - - self.close() - return 0 - - def run_http_or_https(self, node_port, dryrun): - if not dryrun: - # send reboot signal. - # TODO: send a ManualPCU() reboot request for this PCU. - # NOTE: this model defies automation because, the port numbering - # and the form numbers are not consistent across models. There is - # not direct mapping from port# to form#. - return "Manual Reboot Required" - - else: - # TODO: also send message for https, since that doesn't work this way... - if self.type == Transport.HTTPS: - cmd = self.get_https_cmd() - elif self.type == Transport.HTTP: - cmd = self.get_http_cmd() - else: - raise ExceptionNoTransport("Unsupported transport for http command") - - cmd = cmd % ( self.username, self.password, self.host) - print "CMD: %s" % cmd - - p = os.popen(cmd) - result = p.read() - if len(result.split('\n')) > 2: - self.logout() - return 0 - else: - # NOTE: an error has occurred, so no need to log out. - print "RESULT: ", result - return result - - def get_https_cmd(self): - version = self.get_version() - print "VERSION: %s" % version - if "AP96" in version: - cmd = "curl -s --insecure --user '%s:%s' https://%s/outlets.htm " + \ - " | grep -E '^[^<]+' " + \ - " | grep -v 'Protected Object' " - else: - # NOTE: no other case known right now... - cmd = "curl -s --insecure --user '%s:%s' https://%s/outlets.htm " + \ - " | grep -E '^[^<]+' " + \ - " | grep -v 'Protected Object' " - - return cmd - - def get_http_cmd(self): - version = self.get_version() - print "VERSION: %s" % version - if "AP7900" in version: - cmd = "curl -s --anyauth --user '%s:%s' http://%s/rPDUout.htm | grep -E '^[^<]+'" - elif "AP7920" in version: - cmd = "curl -s --anyauth --user '%s:%s' http://%s/ms3out.htm | grep -E '^[^<]+' " - else: - # default case... - print "USING DEFAULT" - cmd = "curl -s --anyauth --user '%s:%s' http://%s/ms3out.htm | grep -E '^[^<]+' " - - return cmd - - def get_version(self): - # NOTE: this command returns and formats all data. - #cmd = """curl -s --anyauth --user '%s:%s' http://%s/about.htm """ + - # """ | sed -e "s/<[^>]*>//g" -e "s/ //g" -e "/^$/d" """ + - # """ | awk '{line=$0 ; if ( ! /:/ && length(pline) > 0 ) \ - # { print pline, line } else { pline=line} }' """ + - # """ | grep Model """ - - # NOTE: we may need to return software version, no model version to - # know which file to request on the server. - - if self.type == Transport.HTTP: - cmd = """curl -s --anyauth --user '%s:%s' http://%s/about.htm """ + \ - """ | sed -e "s/<[^>]*>//g" -e "s/ //g" -e "/^$/d" """ + \ - """ | grep -E "AP[[:digit:]]+" """ - #""" | grep -E "v[[:digit:]].*" """ - elif self.type == Transport.HTTPS: - cmd = """curl -s --insecure --user '%s:%s' https://%s/about.htm """ + \ - """ | sed -e "s/<[^>]*>//g" -e "s/ //g" -e "/^$/d" """ + \ - """ | grep -E "AP[[:digit:]]+" """ - #""" | grep -E "v[[:digit:]].*" """ - else: - raise ExceptionNoTransport("Unsupported transport to get version") - - cmd = cmd % ( self.username, self.password, self.host) - p = os.popen(cmd) - result = p.read() - return result.strip() - - def logout(self): - # NOTE: log out again, to allow other uses to access the machine. - if self.type == Transport.HTTP: - cmd = """curl -s --anyauth --user '%s:%s' http://%s/logout.htm """ + \ - """ | grep -E '^[^<]+' """ - elif self.type == Transport.HTTPS: - cmd = """curl -s --insecure --user '%s:%s' http://%s/logout.htm """ + \ - """ | grep -E '^[^<]+' """ - else: - raise ExceptionNoTransport("Unsupported transport to logout") - - cmd = cmd % ( self.username, self.password, self.host) - p = os.popen(cmd) - print p.read() - -class APCControl12p3(APCControl): - def run(self, node_port, dryrun): - self.reboot_sequence = ["1", "2", str(node_port), "3"] - return super(APCControl12p3, self).run(node_port, dryrun) - -class APCControl1p4(APCControl): - def run(self, node_port, dryrun): - self.reboot_sequence = ["1", str(node_port), "4"] - return super(APCControl1p4, self).run(node_port, dryrun) - -class APCControl121p3(APCControl): - def run(self, node_port, dryrun): - self.reboot_sequence = ["1", "2", "1", str(node_port), "3"] - return super(APCControl121p3, self).run(node_port, dryrun) - -class APCControl121p1(APCControl): - def run(self, node_port, dryrun): - self.reboot_sequence = ["1", "2", "1", str(node_port), "1", "3"] - return super(APCControl121p1, self).run(node_port, dryrun) - -class APCControl13p13(APCControl): - def run(self, node_port, dryrun): - self.reboot_sequence = ["1", "3", str(node_port), "1", "3"] - return super(APCControl13p13, self).run(node_port, dryrun) - - -class IntelAMT(PCUControl): - supported_ports = [16992] - - def run(self, node_port, dryrun): - - cmd = command.CMD() - # TODO: need to make this path universal; not relative to pwd. - cmd_str = config.MONITOR_SCRIPT_ROOT + "/pcucontrol/models/intelamt/remoteControl" - - if dryrun: - # NOTE: -p checks the power state of the host. - # TODO: parse the output to find out if it's ok or not. - cmd_str += " -p http://%s:16992/RemoteControlService -user admin -pass '%s' " % (self.host, self.password ) - else: - cmd_str += " -A http://%s:16992/RemoteControlService -user admin -pass '%s' " % (self.host, self.password ) - - print cmd_str - return cmd.system(cmd_str, self.TELNET_TIMEOUT) - -class DRAC(PCUControl): - supported_ports = [22,443,5869] - def run(self, node_port, dryrun): - if self.type == Transport.DRAC: - print "trying racadm_reboot..." - return racadm_reboot(self.host, self.username, self.password, node_port, dryrun) - elif self.type == Transport.SSH: - return self.run_ssh(node_port, dryrun) - else: - raise ExceptionNoTransport("No implementation for open ports") - - def run_ssh(self, node_port, dryrun): - ssh_options="-o StrictHostKeyChecking=no "+\ - "-o PasswordAuthentication=yes "+\ - "-o PubkeyAuthentication=no" - s = pxssh.pxssh() - if not s.login(self.host, self.username, self.password, ssh_options, - original_prompts="Dell", login_timeout=TELNET_TIMEOUT): - raise ExceptionPassword("Invalid Password") - - print "logging in..." - s.send("\r\n\r\n") - try: - # Testing Reboot ? - #index = s.expect(["DRAC 5", "[%s]#" % self.username ]) - # NOTE: be careful to escape any characters used by 're.compile' - index = s.expect(["\$", "\[%s\]#" % self.username ]) - print "INDEX:", index - if dryrun: - if index == 0: - s.send("racadm getsysinfo") - elif index == 1: - s.send("getsysinfo") - else: - if index == 0: - s.send("racadm serveraction powercycle") - elif index == 1: - s.send("serveraction powercycle") - - s.send("exit") - - except pexpect.EOF: - raise ExceptionPrompt("EOF before expected Prompt") - except pexpect.TIMEOUT: - print s - raise ExceptionPrompt("Timeout before expected Prompt") - - s.close() - - return 0 - -class DRACDefault(PCUControl): - supported_ports = [22,443,5869] - def run(self, node_port, dryrun): - self.open(self.host, self.username) - self.sendPassword(self.password) - - print "logging in..." - self.transport.write("\r\n") - # Testing Reboot ? - if dryrun: - self.ifThenSend("[%s]#" % self.username, "getsysinfo") - else: - # Reset this machine - self.ifThenSend("[%s]#" % self.username, "serveraction powercycle") - - self.ifThenSend("[%s]#" % self.username, "exit") - - self.close() - return 0 - -class HPiLO(PCUControl): - supported_ports = [22,443] - def run(self, node_port, dryrun): - if self.type == Transport.SSH: - return self.run_ssh(node_port, dryrun) - elif self.type == Transport.HTTP or self.type == Transport.HTTPS: - return self.run_https(node_port, dryrun) - else: - raise ExceptionNoTransport("Unimplemented Transport for HPiLO %s" % self.type) - - def run_ssh(self, node_port, dryrun): - - self.open(self.host, self.username) - self.sendPassword(self.password) - - # hpiLO-> - self.ifThenSend("hpiLO->", "cd system1") - - # Reboot Outlet N (Y/N)? - if dryrun: - self.ifThenSend("hpiLO->", "POWER") - else: - # Reset this machine - self.ifThenSend("hpiLO->", "reset") - - self.ifThenSend("hpiLO->", "exit") - - self.close() - return 0 - - def run_https(self, node_port, dryrun): - - locfg = command.CMD() - - cmd_str = config.MONITOR_SCRIPT_ROOT + "/pcucontrol/models/hpilo/" - - cmd = cmd_str + "locfg.pl -s %s -f %s -u %s -p '%s' | grep 'MESSAGE' | grep -v 'No error'" % ( - self.host, cmd_str+"iloxml/Get_Network.xml", - self.username, self.password) - sout, serr = locfg.run_noexcept(cmd) - - if sout.strip() != "" or serr.strip() != "": - print "sout: %s" % sout.strip() - return sout.strip() + serr.strip() - - if not dryrun: - locfg = command.CMD() - cmd = cmd_str + "locfg.pl -s %s -f %s -u %s -p '%s' | grep 'MESSAGE' | grep -v 'No error'" % ( - self.host, cmd_str+"iloxml/Reset_Server.xml", - self.username, self.password) - sout, serr = locfg.run_noexcept(cmd) - - if sout.strip() != "": - print "sout: %s" % sout.strip() - #return sout.strip() - - return 0 - -class BayTechRPC3NC(PCUControl): - def run(self, node_port, dryrun): - self.open(self.host, self.username, None, "Enter user name:") - self.sendPassword(self.password, "Enter Password:") - - #self.ifThenSend("RPC-16>", "Status") - self.ifThenSend("RPC3-NC>", "Reboot %d" % node_port) - - # Reboot Outlet N (Y/N)? - if dryrun: - self.ifThenSend("(Y/N)?", "N") - else: - self.ifThenSend("(Y/N)?", "Y") - self.ifThenSend("RPC3-NC>", "") - - self.close() - return 0 - -class BayTechRPC16(PCUControl): - def run(self, node_port, dryrun): - self.open(self.host, self.username, None, "Enter user name:") - self.sendPassword(self.password, "Enter Password:") - - #self.ifThenSend("RPC-16>", "Status") - - self.ifThenSend("RPC-16>", "Reboot %d" % node_port) - - # Reboot Outlet N (Y/N)? - if dryrun: - self.ifThenSend("(Y/N)?", "N") - else: - self.ifThenSend("(Y/N)?", "Y") - self.ifThenSend("RPC-16>", "") - - self.close() - return 0 - -class BayTechCtrlCUnibe(PCUControl): - """ - For some reason, these units let you log in fine, but they hang - indefinitely, unless you send a Ctrl-C after the password. No idea - why. - """ - def run(self, node_port, dryrun): - print "BayTechCtrlC %s" % self.host - - ssh_options="-o StrictHostKeyChecking=no -o PasswordAuthentication=yes -o PubkeyAuthentication=no" - s = pxssh.pxssh() - if not s.login(self.host, self.username, self.password, ssh_options): - raise ExceptionPassword("Invalid Password") - # Otherwise, the login succeeded. - - # Send a ctrl-c to the remote process. - print "sending ctrl-c" - s.send(chr(3)) - - # Control Outlets (5 ,1).........5 - try: - #index = s.expect("Enter Request") - index = s.expect(["Enter Request :"]) - - if index == 0: - print "3" - s.send("3\r\n") - index = s.expect(["DS-RPC>", "Enter user name:"]) - if index == 1: - s.send(self.username + "\r\n") - index = s.expect(["DS-RPC>"]) - - if index == 0: - print "Reboot %d" % node_port - time.sleep(5) - s.send("Reboot %d\r\n" % node_port) - - time.sleep(5) - index = s.expect(["\(Y/N\)\?", "Port in use", "DS-RPC>"]) - if index == 0: - if dryrun: - print "sending N" - s.send("N\r\n") - else: - print "sending Y" - s.send("Y\r\n") - elif index == 1: - raise ExceptionPrompt("PCU Reported 'Port in use.'") - elif index == 2: - raise ExceptionSequence("Issued command 'Reboot' failed.") - - time.sleep(5) - index = s.expect(["DS-RPC>"]) - #print "got prompt back" - - s.close() - - except pexpect.EOF: - raise ExceptionPrompt("EOF before expected Prompt") - except pexpect.TIMEOUT: - raise ExceptionPrompt("Timeout before expected Prompt") - - return 0 - -class BayTechCtrlC(PCUControl): - """ - For some reason, these units let you log in fine, but they hang - indefinitely, unless you send a Ctrl-C after the password. No idea - why. - """ - def run(self, node_port, dryrun): - print "BayTechCtrlC %s" % self.host - - ssh_options="-o StrictHostKeyChecking=no -o PasswordAuthentication=yes -o PubkeyAuthentication=no" - s = pxssh.pxssh() - if not s.login(self.host, self.username, self.password, ssh_options): - raise ExceptionPassword("Invalid Password") - # Otherwise, the login succeeded. - - # Send a ctrl-c to the remote process. - print "SENDING ctrl-c" - s.send(chr(3)) - - # Control Outlets (5 ,1).........5 - try: - print "EXPECTING: ", "Enter Request :" - index = s.expect(["Enter Request :"]) - - if index == 0: - print "SENDING: 5" - s.send("5\r\n") - print "EXPECTING: ", "DS-RPC>" - index = s.expect(["DS-RPC>", "Enter user name:", "Port in use."]) - if index == 1: - print "sending username" - s.send(self.username + "\r\n") - index = s.expect(["DS-RPC>"]) - elif index == 2: - raise ExceptionPrompt("PCU Reported 'Port in use.'") - - if index == 0: - print "SENDING: Reboot %d" % node_port - s.send("Reboot %d\r\n" % node_port) - - print "SLEEPING: 5" - time.sleep(5) - print "EXPECTING: ", "Y/N?" - index = s.expect(["\(Y/N\)\?", "Port in use", "DS-RPC>"]) - if index == 0: - if dryrun: - print "sending N" - s.send("N\r\n") - else: - print "SENDING: Y" - s.send("Y\r\n") - elif index == 1: - raise ExceptionPrompt("PCU Reported 'Port in use.'") - elif index == 2: - raise ExceptionSequence("Issued command 'Reboot' failed.") - - # NOTE: for some reason, the script times out with the - # following line. In manual tests, it works correctly, but - # with automated tests, evidently it fails. - print "SLEEPING: 5" - time.sleep(5) - #print "TOTAL--", s.allstr, "--EOT" - index = s.expect(["DS-RPC>"]) - print "got prompt back" - - s.close() - - except pexpect.EOF: - raise ExceptionPrompt("EOF before 'Enter Request' Prompt") - except pexpect.TIMEOUT: - raise ExceptionPrompt("Timeout before Prompt") - - return 0 - -class BayTech(PCUControl): - supported_ports = [22,23] - def run(self, node_port, dryrun): - self.open(self.host, self.username) - self.sendPassword(self.password) - - # Control Outlets (5 ,1).........5 - self.ifThenSend("Enter Request :", "5") - - # Reboot N - try: - self.ifThenSend("DS-RPC>", "Reboot %d" % node_port, ExceptionNotFound) - except ExceptionNotFound, msg: - # one machine is configured to ask for a username, - # even after login... - print "msg: %s" % msg - self.transport.write(self.username + "\r\n") - time.sleep(5) - self.ifThenSend("DS-RPC>", "Reboot %d" % node_port) - - # Reboot Outlet N (Y/N)? - if dryrun: - self.ifThenSend("(Y/N)?", "N") - else: - self.ifThenSend("(Y/N)?", "Y") - time.sleep(5) - self.ifThenSend("DS-RPC>", "") - - self.close() - return 0 - -class WTIIPS4(PCUControl): - supported_ports = [23] - def run(self, node_port, dryrun): - self.open(self.host) - self.sendPassword(self.password, "Enter Password:") - - self.ifThenSend("IPS> ", "/Boot %s" % node_port) - if not dryrun: - self.ifThenSend("Sure? (Y/N): ", "N") - else: - self.ifThenSend("Sure? (Y/N): ", "Y") - - self.ifThenSend("IPS> ", "") - - self.close() - return 0 - -class ePowerSwitchNew(PCUControl): - # NOTE: - # The old code used Python's HTTPPasswordMgrWithDefaultRealm() - # For some reason this both doesn't work and in some cases, actually - # hangs the PCU. Definitely not what we want. - # - # The code below is much simpler. Just letting things fail first, - # and then, trying again with authentication string in the header. - # - def run(self, node_port, dryrun): - self.transport = None - self.url = "http://%s:%d/" % (self.host,80) - uri = "%s:%d" % (self.host,80) - - req = urllib2.Request(self.url) - try: - handle = urllib2.urlopen(req) - except IOError, e: - # NOTE: this is expected to fail initially - pass - else: - print self.url - print "-----------" - print handle.read() - print "-----------" - return "ERROR: not protected by HTTP authentication" - - if not hasattr(e, 'code') or e.code != 401: - return "ERROR: failed for: %s" % str(e) - - base64data = base64.encodestring("%s:%s" % (self.username, self.password))[:-1] - # NOTE: assuming basic realm authentication. - authheader = "Basic %s" % base64data - req.add_header("Authorization", authheader) - - try: - f = urllib2.urlopen(req) - except IOError, e: - # failing here means the User/passwd is wrong (hopefully) - raise ExceptionPassword("Incorrect username/password") - - # NOTE: after verifying that the user/password is correct, - # actually reboot the given node. - if not dryrun: - try: - data = urllib.urlencode({'P%d' % node_port : "r"}) - req = urllib2.Request(self.url + "cmd.html") - req.add_header("Authorization", authheader) - # add data to handler, - f = urllib2.urlopen(req, data) - if self.verbose: print f.read() - except: - import traceback; traceback.print_exc() - - # fetch url one more time on cmd.html, econtrol.html or whatever. - # pass - else: - if self.verbose: print f.read() - - self.close() - return 0 - -class CustomPCU(PCUControl): - def run(self, node_port, dryrun): - url = "https://www-itec.uni-klu.ac.at/plab-pcu/index.php" - - if not dryrun: - # Turn host off, then on - formstr = "plab%s=off" % node_port - os.system("curl --user %s:%s --form '%s' --insecure %s" % (self.username, self.password, formstr, url)) - time.sleep(5) - formstr = "plab%s=on" % node_port - os.system("curl --user %s:%s --form '%s' --insecure %s" % (self.username, self.password, formstr, url)) - else: - os.system("curl --user %s:%s --insecure %s" % (self.username, self.password, url)) - - -class ePowerSwitchOld(PCUControl): - def run(self, node_port, dryrun): - self.url = "http://%s:%d/" % (self.host,80) - uri = "%s:%d" % (self.host,80) - - # create authinfo - authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm() - authinfo.add_password (None, uri, self.username, self.password) - authhandler = urllib2.HTTPBasicAuthHandler( authinfo ) - - # NOTE: it doesn't seem to matter whether this authinfo is here or not. - transport = urllib2.build_opener(authinfo) - f = transport.open(self.url) - if self.verbose: print f.read() - - if not dryrun: - transport = urllib2.build_opener(authhandler) - f = transport.open(self.url + "cmd.html", "P%d=r" % node_port) - if self.verbose: print f.read() - - self.close() - return 0 - -class ePowerSwitchOld(PCUControl): - supported_ports = [80] - def run(self, node_port, dryrun): - self.url = "http://%s:%d/" % (self.host,80) - uri = "%s:%d" % (self.host,80) - - # TODO: I'm still not sure what the deal is here. - # two independent calls appear to need to be made before the - # reboot will succeed. It doesn't seem to be possible to do - # this with a single call. I have no idea why. - - # create authinfo - authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm() - authinfo.add_password (None, uri, self.username, self.password) - authhandler = urllib2.HTTPBasicAuthHandler( authinfo ) - - # NOTE: it doesn't seem to matter whether this authinfo is here or not. - transport = urllib2.build_opener() - f = transport.open(self.url + "elogin.html", "pwd=%s" % self.password) - if self.verbose: print f.read() - - if not dryrun: - transport = urllib2.build_opener(authhandler) - f = transport.open(self.url + "econtrol.html", "P%d=r" % node_port) - if self.verbose: print f.read() - - # data= "P%d=r" % node_port - #self.open(self.host, self.username, self.password) - #self.sendHTTP("elogin.html", "pwd=%s" % self.password) - #self.sendHTTP("econtrol.html", data) - #self.sendHTTP("cmd.html", data) - - self.close() - return 0 - -class ManualPCU(PCUControl): - supported_ports = [22,23,80,443] - - def run(self, node_port, dryrun): - if not dryrun: - # TODO: send email message to monitor admin requesting manual - # intervention. This should always be an option for ridiculous, - # custom jobs. - pass - return 0 - -class PM211MIP(ManualPCU): - supported_ports = [80,443] - -### rebooting european BlackBox PSE boxes -# Thierry Parmentelat - May 11 2005 -# tested on 4-ports models known as PSE505-FR -# uses http to POST a data 'P=r' -# relies on basic authentication within http1.0 -# first curl-based script was -# curl --http1.0 --basic --user : --data P=r \ -# http://:/cmd.html && echo OK - -# log in: - -## BB PSMaverick -class BlackBoxPSMaverick(PCUControl): - supported_ports = [80] - - def run(self, node_port, dryrun): - if not dryrun: - # send reboot signal. - cmd = "curl -s --data 'P%s=r' --anyauth --user '%s:%s' http://%s/config/home_f.html" % ( node_port, self.username, self.password, self.host) - else: - # else, just try to log in - cmd = "curl -s --anyauth --user '%s:%s' http://%s/config/home_f.html" % ( self.username, self.password, self.host) - - p = os.popen(cmd) - result = p.read() - print "RESULT: ", result - - if len(result.split()) > 3: - return 0 - else: - return result - -def bbpse_reboot (pcu_ip,username,password,port_in_pcu,http_port, dryrun): - - global verbose - - url = "http://%s:%d/cmd.html" % (pcu_ip,http_port) - data= "P%d=r" % port_in_pcu - if verbose: - logger.debug("POSTing '%s' on %s" % (data,url)) - - authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm() - uri = "%s:%d" % (pcu_ip,http_port) - authinfo.add_password (None, uri, username, password) - authhandler = urllib2.HTTPBasicAuthHandler( authinfo ) - - opener = urllib2.build_opener(authhandler) - urllib2.install_opener(opener) - - if (dryrun): - return 0 - - try: - f = urllib2.urlopen(url,data) - - r= f.read() - if verbose: - logger.debug(r) - return 0 - - except urllib2.URLError,err: - logger.info('Could not open http connection', err) - return "bbpse error" - -### rebooting x10toggle based systems addressed by port -# Marc E. Fiuczynski - May 31 2005 -# tested on 4-ports models known as PSE505-FR -# uses ssh and password to login to an account -# that will cause the system to be powercycled. - -def x10toggle_reboot(ip, username, password, port, dryrun): - global verbose - - ssh = None - try: - ssh = pyssh.Ssh(username, ip) - ssh.open() - - # Login - telnet_answer(ssh, "password:", password) - - if not dryrun: - # Reboot - telnet_answer(ssh, "x10toggle>", "A%d" % port) - - # Close - output = ssh.close() - if verbose: - logger.debug(output) - return 0 - - except Exception, err: - if verbose: - logger.debug(err) - if ssh: - output = ssh.close() - if verbose: - logger.debug(output) - return errno.ETIMEDOUT - -### rebooting Dell systems via RAC card -# Marc E. Fiuczynski - June 01 2005 -# tested with David Lowenthal's itchy/scratchy nodes at UGA -# - -def runcmd(command, args, username, password, timeout = None): - - result = [None] - result_ready = threading.Condition() - - def set_result(x): - - result_ready.acquire() - try: - result[0] = x - finally: - result_ready.notify() - result_ready.release() - - def do_command(command, username, password): - - try: - # Popen4 is a popen-type class that combines stdout and stderr - p = popen2.Popen4(command) - - # read all output data - p.tochild.write("%s\n" % username) - p.tochild.write("%s\n" % password) - p.tochild.close() - data = p.fromchild.read() - - while True: - # might get interrupted by a signal in poll() or waitpid() - try: - retval = p.wait() - set_result((retval, data)) - break - except OSError, ex: - if ex.errno == errno.EINTR: - continue - raise ex - except Exception, ex: - set_result(ex) - - if args: - command = " ".join([command] + args) - - worker = threading.Thread(target = do_command, args = (command, username, password, )) - worker.setDaemon(True) - result_ready.acquire() - worker.start() - result_ready.wait(timeout) - try: - if result == [None]: - raise Exception, "command timed-out: '%s'" % command - finally: - result_ready.release() - result = result[0] - - if isinstance(result, Exception): - raise result - else: - (retval, data) = result - if os.WIFEXITED(retval) and os.WEXITSTATUS(retval) == 0: - return data - else: - out = "system command ('%s') " % command - if os.WIFEXITED(retval): - out += "failed, rc = %d" % os.WEXITSTATUS(retval) - else: - out += "killed by signal %d" % os.WTERMSIG(retval) - if data: - out += "; output follows:\n" + data - raise Exception, out - -def racadm_reboot(host, username, password, port, dryrun): - global verbose - - ip = socket.gethostbyname(host) - try: - cmd = "/usr/sbin/racadm" - os.stat(cmd) - if not dryrun: - output = runcmd(cmd, ["-r %s -i serveraction powercycle" % ip], - username, password) - else: - output = runcmd(cmd, ["-r %s -i getsysinfo" % ip], - username, password) - - print "RUNCMD: %s" % output - if verbose: - logger.debug(output) - return 0 - - except Exception, err: - logger.debug("runcmd raised exception %s" % err) - if verbose: - logger.debug(err) - return err +from pcucontrol.models import * def pcu_name(pcu): if pcu['hostname'] is not None and pcu['hostname'] is not "": @@ -1351,7 +335,6 @@ def pcu_name(pcu): else: return None - def get_pcu_values(pcu_id): print "pcuid: %s" % pcu_id try: @@ -1452,172 +435,44 @@ def model_to_object(modelname): print "UNKNOWN model %s"%modelname return Unknown -def reboot_test_new(nodename, values, verbose, dryrun): +def reboot_api(node, pcu): #, verbose, dryrun): rb_ret = "" - if 'plc_pcu_stats' in values: - values.update(values['plc_pcu_stats']) try: - modelname = values['model'] + modelname = pcu['model'] if modelname: - object = eval('%s(values, verbose, ["22", "23", "80", "443", "9100", "16992", "5869"])' % modelname) - rb_ret = object.reboot(values[nodename], dryrun) - else: - rb_ret = "Not_Run" + # get object instance + instance = eval('%s(pcu, verbose)' % modelname) + # get pcu port + i = pcu['node_ids'].index(node['node_id']) + p = pcu['ports'][i] + # reboot + rb_ret = instance.reboot(p, False) + else: + rb_ret = "No modelname in PCU record." # TODO: how to handle the weird, georgetown pcus, the drac faults, and ilo faults - except ExceptionPort, err: + except Exception, err: rb_ret = str(err) return rb_ret - -def reboot_test(nodename, values, continue_probe, verbose, dryrun): +def reboot_test_new(nodename, values, verbose, dryrun): rb_ret = "" if 'plc_pcu_stats' in values: values.update(values['plc_pcu_stats']) try: - # DataProbe iPal (many sites) - if continue_probe and values['model'].find("IP-41x_IP-81x") >= 0: - ipal = IPAL(values, verbose, ['23', '80', '9100']) - rb_ret = ipal.reboot(values[nodename], dryrun) - - # APC Masterswitch (Berkeley) - elif continue_probe and ( values['model'].find("AP79xx") >= 0 or \ - values['model'].find("Masterswitch") >= 0 ): - print values - - # TODO: make a more robust version of APC - if values['pcu_id'] in [1102,1163,1055,1111,1231,1113,1127,1128,1148]: - apc = APCControl12p3(values, verbose, ['22', '23']) - rb_ret = apc.reboot(values[nodename], dryrun) - - elif values['pcu_id'] in [1110,86]: - apc = APCControl1p4(values, verbose, ['22', '23']) - rb_ret = apc.reboot(values[nodename], dryrun) - - elif values['pcu_id'] in [1221,1225,1220,1192]: - apc = APCControl121p3(values, verbose, ['22', '23']) - rb_ret = apc.reboot(values[nodename], dryrun) - - elif values['pcu_id'] in [1173,1240,47,1363,1405,1401,1372,1371]: - apc = APCControl121p1(values, verbose, ['22', '23']) - rb_ret = apc.reboot(values[nodename], dryrun) - - else: - apc = APCControl13p13(values, verbose, ['22', '23']) - rb_ret = apc.reboot(values[nodename], dryrun) - - # BayTech DS4-RPC - elif continue_probe and values['model'].find("DS4-RPC") >= 0: - if values['pcu_id'] in [1056,1237,1052,1209,1002,1008,1041,1013,1022]: - # These require a 'ctrl-c' to be sent... - baytech = BayTechCtrlC(values, verbose, ['22', '23']) - rb_ret = baytech.reboot(values[nodename], dryrun) - - elif values['pcu_id'] in [93]: - baytech = BayTechRPC3NC(values, verbose, ['22', '23']) - rb_ret = baytech.reboot(values[nodename], dryrun) - - elif values['pcu_id'] in [1057]: - # These require a 'ctrl-c' to be sent... - baytech = BayTechCtrlCUnibe(values, verbose, ['22', '23']) - rb_ret = baytech.reboot(values[nodename], dryrun) - - elif values['pcu_id'] in [1012]: - # This pcu sometimes doesn't present the 'Username' prompt, - # unless you immediately try again... - try: - baytech = BayTechRPC16(values, verbose, ['22', '23']) - rb_ret = baytech.reboot(values[nodename], dryrun) - except: - baytech = BayTechRPC16(values, verbose, ['22', '23']) - rb_ret = baytech.reboot(values[nodename], dryrun) - else: - baytech = BayTech(values, verbose, ['22', '23']) - rb_ret = baytech.reboot(values[nodename], dryrun) - - # iLO - elif continue_probe and values['model'].find("ilo") >= 0: - try: - hpilo = HPiLO(values, verbose, ['22']) - rb_ret = hpilo.reboot(0, dryrun) - if rb_ret != 0: - hpilo = HPiLOHttps(values, verbose, ['443']) - rb_ret = hpilo.reboot(0, dryrun) - except: - hpilo = HPiLOHttps(values, verbose, ['443']) - rb_ret = hpilo.reboot(0, dryrun) - - # DRAC ssh - elif continue_probe and values['model'].find("DRAC") >= 0: - # TODO: I don't think DRAC will throw an exception for the - # default method to catch... - try: - drac = DRAC(values, verbose, ['443', '5869']) - rb_ret = drac.reboot(0, dryrun) - except: - drac = DRACDefault(values, verbose, ['22']) - rb_ret = drac.reboot(0, dryrun) - - elif continue_probe and values['model'].find("WTI IPS-4") >= 0: - wti = WTIIPS4(values, verbose, ['23']) - rb_ret = wti.reboot(values[nodename], dryrun) - - elif continue_probe and values['model'].find("AMT") >= 0: - amt = IntelAMT(values, verbose, ['16992']) - rb_ret = amt.reboot(values[nodename], dryrun) - - elif continue_probe and values['model'].find("bbsemaverick") >=0: - print "TRYING BlackBoxPSMaverick" - bbe = BlackBoxPSMaverick(values, verbose, ['80']) - rb_ret = bbe.reboot(values[nodename], dryrun) - - elif continue_probe and values['model'].find("ipmi") >=0: - - print "TRYING IPMI" - ipmi = IPMI(values, verbose, ['80', '443', '623']) - rb_ret = ipmi.reboot(values[nodename], dryrun) - - elif continue_probe and values['model'].find("ePowerSwitch") >=0: - # TODO: allow a different port than http 80. - if values['pcu_id'] in [1089, 1071, 1046, 1035, 1118]: - eps = ePowerSwitchNew(values, verbose, ['80']) - elif values['pcu_id'] in [1003]: - # OLD EPOWER - print "OLD EPOWER" - eps = ePowerSwitchOld(values, verbose, ['80']) - else: - eps = ePowerSwitchNew(values, verbose, ['80']) - - rb_ret = eps.reboot(values[nodename], dryrun) - elif continue_probe and values['pcu_id'] in [1122]: - custom = CustomPCU(values, verbose, ['80', '443']) - custom.reboot(values[nodename], dryrun) - - elif continue_probe: - rb_ret = "Unsupported_PCU" - - elif continue_probe == False: - if 'port_status' in values: - rb_ret = "NetDown" - else: - rb_ret = "Not_Run" + modelname = values['model'] + if modelname: + object = eval('%s(values, verbose, ["22", "23", "80", "443", "9100", "16992", "5869"])' % modelname) + rb_ret = object.reboot(values[nodename], dryrun) else: - rb_ret = -1 - + rb_ret = "Not_Run" + # TODO: how to handle the weird, georgetown pcus, the drac faults, and ilo faults except ExceptionPort, err: rb_ret = str(err) return rb_ret - # ???? - #elif continue_probe and values['protocol'] == "racadm" and \ - # values['model'] == "RAC": - # rb_ret = racadm_reboot(pcu_name(values), - # values['username'], - # values['password'], - # pcu[nodename], - # dryrun) def main(): logger.setLevel(logging.DEBUG) -- 2.43.0