From: Stephen Soltesz Date: Tue, 6 Jan 2009 19:22:26 +0000 (+0000) Subject: PCU Class objects broken out into individual files. X-Git-Tag: Monitor-2.0-1~31 X-Git-Url: http://git.onelab.eu/?p=monitor.git;a=commitdiff_plain;h=311f1adfd6750378d80e4c8c7eb2a8a251d4ce7e PCU Class objects broken out into individual files. --- diff --git a/pcucontrol/models/APCControl.py b/pcucontrol/models/APCControl.py new file mode 100644 index 0000000..62f5f6f --- /dev/null +++ b/pcucontrol/models/APCControl.py @@ -0,0 +1,177 @@ +from pcucontrol.reboot import * + +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_ssh(self, node_port, dryrun): + return self.run_telnet_or_ssh(node_port, dryrun) + def run_telnet(self, node_port, dryrun): + return self.run_telnet_or_ssh(node_port, dryrun) + + def run_telnet_or_ssh(self, node_port, dryrun): + self.transport.open(self.host, self.username) + self.transport.sendPassword(self.password) + + first = True + for val in self.reboot_sequence: + if first: + self.transport.ifThenSend("\r\n> ", val, ExceptionPassword) + first = False + else: + self.transport.ifThenSend("\r\n> ", val) + + if not dryrun: + self.transport.ifThenSend("Enter 'YES' to continue or to cancel", + "YES\r\n", + ExceptionSequence) + else: + self.transport.ifThenSend("Enter 'YES' to continue or to cancel", + "", ExceptionSequence) + self.transport.ifThenSend("Press to continue...", "", ExceptionSequence) + + self.transport.close() + return 0 + + def run_http(self, node_port, dryrun): + return self.run_http_or_https(node_port, dryrun) + def run_https(self, node_port, dryrun): + return self.run_http_or_https(node_port, dryrun) + + 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_telnet_or_ssh(self, node_port, dryrun): + self.reboot_sequence = ["1", "2", str(node_port), "3"] + return super(APCControl12p3, self).run_telnet_or_ssh(node_port, dryrun) + +class APCControl1p4(APCControl): + def run_telnet_or_ssh(self, node_port, dryrun): + self.reboot_sequence = ["1", str(node_port), "4"] + return super(APCControl1p4, self).run_telnet_or_ssh(node_port, dryrun) + +class APCControl121p3(APCControl): + def run_telnet_or_ssh(self, node_port, dryrun): + self.reboot_sequence = ["1", "2", "1", str(node_port), "3"] + return super(APCControl121p3, self).run_telnet_or_ssh(node_port, dryrun) + +class APCControl121p1(APCControl): + def run_telnet_or_ssh(self, node_port, dryrun): + self.reboot_sequence = ["1", "2", "1", str(node_port), "1", "3"] + return super(APCControl121p1, self).run_telnet_or_ssh(node_port, dryrun) + +class APCControl13p13(APCControl): + def run_telnet_or_ssh(self, node_port, dryrun): + self.reboot_sequence = ["1", "3", str(node_port), "1", "3"] + return super(APCControl13p13, self).run_telnet_or_ssh(node_port, dryrun) diff --git a/pcucontrol/models/BayTech.py b/pcucontrol/models/BayTech.py new file mode 100644 index 0000000..83de3a5 --- /dev/null +++ b/pcucontrol/models/BayTech.py @@ -0,0 +1,216 @@ +from pcucontrol.reboot import * + +class BayTechRPC3NC(PCUControl): + def run_telnet(self, node_port, dryrun): + return self.run_ssh(node_port, dryrun) + + def run_ssh(self, node_port, dryrun): + self.transport.open(self.host, self.username, None, "Enter user name:") + self.transport.sendPassword(self.password, "Enter Password:") + + #self.transport.ifThenSend("RPC-16>", "Status") + self.transport.ifThenSend("RPC3-NC>", "Reboot %d" % node_port) + + # Reboot Outlet N (Y/N)? + if dryrun: + self.transport.ifThenSend("(Y/N)?", "N") + else: + self.transport.ifThenSend("(Y/N)?", "Y") + self.transport.ifThenSend("RPC3-NC>", "") + + self.transport.close() + return 0 + +class BayTechRPC16(PCUControl): + def run_telnet(self, node_port, dryrun): + return self.run_ssh(node_port, dryrun) + def run_ssh(self, node_port, dryrun): + self.transport.open(self.host, self.username, None, "Enter user name:") + self.transport.sendPassword(self.password, "Enter Password:") + + #self.transport.ifThenSend("RPC-16>", "Status") + + self.transport.ifThenSend("RPC-16>", "Reboot %d" % node_port) + + # Reboot Outlet N (Y/N)? + if dryrun: + self.transport.ifThenSend("(Y/N)?", "N") + else: + self.transport.ifThenSend("(Y/N)?", "Y") + self.transport.ifThenSend("RPC-16>", "") + + self.transport.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_ssh(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_ssh(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_telnet(self, node_port, dryrun): + return self.run_ssh(node_port, dryrun) + + def run_ssh(self, node_port, dryrun): + self.transport.open(self.host, self.username) + self.transport.sendPassword(self.password) + + # Control Outlets (5 ,1).........5 + self.transport.ifThenSend("Enter Request :", "5") + + # Reboot N + try: + self.transport.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.transport.ifThenSend("DS-RPC>", "Reboot %d" % node_port) + + # Reboot Outlet N (Y/N)? + if dryrun: + self.transport.ifThenSend("(Y/N)?", "N") + else: + self.transport.ifThenSend("(Y/N)?", "Y") + time.sleep(5) + self.transport.ifThenSend("DS-RPC>", "") + + self.transport.close() + return 0 diff --git a/pcucontrol/models/BlackBoxPSMaverick.py b/pcucontrol/models/BlackBoxPSMaverick.py new file mode 100644 index 0000000..ee414dc --- /dev/null +++ b/pcucontrol/models/BlackBoxPSMaverick.py @@ -0,0 +1,65 @@ +from pcucontrol.reboot import * + +### 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_http(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" diff --git a/pcucontrol/models/CustomPCU.py b/pcucontrol/models/CustomPCU.py new file mode 100644 index 0000000..60de348 --- /dev/null +++ b/pcucontrol/models/CustomPCU.py @@ -0,0 +1,26 @@ +# Each method follows the following format: +# CustomPCU_loginbase() +# +# This provides a simple means of lookup given the custom type. +# The only problem might come up if a single site has multiple custom PCUs. +# That would be pretty wierd though... + +from pcucontrol.reboot import * + +class CustomPCU_uniklu(PCUControl): + def run_http(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)) + + + + diff --git a/pcucontrol/models/DRAC.py b/pcucontrol/models/DRAC.py new file mode 100644 index 0000000..a2731cd --- /dev/null +++ b/pcucontrol/models/DRAC.py @@ -0,0 +1,166 @@ +from pcucontrol.reboot import * + +class DRAC(PCUControl): + supported_ports = [22,443,5869] + def run_drac(self, node_port, dryrun): + print "trying racadm_reboot..." + return racadm_reboot(self.host, self.username, self.password, node_port, dryrun) + + 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=Transport.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_ssh(self, node_port, dryrun): + self.transport.open(self.host, self.username) + self.transport.sendPassword(self.password) + + print "logging in..." + self.transport.write("\r\n") + # Testing Reboot ? + if dryrun: + self.transport.ifThenSend("[%s]#" % self.username, "getsysinfo") + else: + # Reset this machine + self.transport.ifThenSend("[%s]#" % self.username, "serveraction powercycle") + + self.transport.ifThenSend("[%s]#" % self.username, "exit") + + self.transport.close() + return 0 + +### 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 diff --git a/pcucontrol/models/HPiLO.py b/pcucontrol/models/HPiLO.py new file mode 100644 index 0000000..25d4331 --- /dev/null +++ b/pcucontrol/models/HPiLO.py @@ -0,0 +1,59 @@ +from pcucontrol.reboot import * + +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.transport.open(self.host, self.username) + self.transport.sendPassword(self.password) + + # hpiLO-> + self.transport.ifThenSend("hpiLO->", "cd system1") + + # Reboot Outlet N (Y/N)? + if dryrun: + self.transport.ifThenSend("hpiLO->", "POWER") + else: + # Reset this machine + self.transport.ifThenSend("hpiLO->", "reset") + + self.transport.ifThenSend("hpiLO->", "exit") + + self.transport.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 diff --git a/pcucontrol/models/IPAL.py b/pcucontrol/models/IPAL.py new file mode 100644 index 0000000..75668db --- /dev/null +++ b/pcucontrol/models/IPAL.py @@ -0,0 +1,142 @@ +from pcucontrol.reboot import * + +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 = [9100,23,80] + + 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.transport.open(self.host) + ## XXX Some iPals require you to hit Enter a few times first + self.transport.ifThenSend("Password >", "\r\n\r\n", ExceptionNotFound) + # Login + self.transport.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.transport.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.transport.ifElse("Enter >", ExceptionTimeout) + print "closing " + self.transport.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 diff --git a/pcucontrol/models/IntelAMT.py b/pcucontrol/models/IntelAMT.py new file mode 100644 index 0000000..af201ba --- /dev/null +++ b/pcucontrol/models/IntelAMT.py @@ -0,0 +1,20 @@ +from pcucontrol.reboot import * + +class IntelAMT(PCUControl): + supported_ports = [16992] + + def run_amt(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, Transport.TELNET_TIMEOUT) diff --git a/pcucontrol/models/ManualPCU.py b/pcucontrol/models/ManualPCU.py new file mode 100644 index 0000000..3a21cd9 --- /dev/null +++ b/pcucontrol/models/ManualPCU.py @@ -0,0 +1,13 @@ +from pcucontrol.reboot import * + +class ManualPCU(PCUControl): + supported_ports = [22,23,80,443] + + def run_http(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 + diff --git a/pcucontrol/models/OpenIPMI.py b/pcucontrol/models/OpenIPMI.py new file mode 100644 index 0000000..f52ea39 --- /dev/null +++ b/pcucontrol/models/OpenIPMI.py @@ -0,0 +1,25 @@ + +from pcucontrol.reboot import * + +class OpenIPMI(PCUControl): + + supported_ports = [80,443,623] + + # TODO: get exit codes to determine success or failure... + def run_https(self, node_port, dryrun): + + if not dryrun: + cmd = "ipmitool -I lanplus -H %s -U %s -P '%s' power cycle " + (i,p) = os.popen4(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 " + (i,p) = os.popen4(cmd % ( self.host, self.username, self.password) ) + result = p.read() + print "RESULT: ", result + + if "Error" in result: + return result + else: + return 0 diff --git a/pcucontrol/models/PM211MIP.py b/pcucontrol/models/PM211MIP.py new file mode 100644 index 0000000..007b307 --- /dev/null +++ b/pcucontrol/models/PM211MIP.py @@ -0,0 +1,5 @@ +from pcucontrol.reboot import * +from ManualPCU import * + +class PM211MIP(ManualPCU): + supported_ports = [80,443] diff --git a/pcucontrol/models/WTIIPS4.py b/pcucontrol/models/WTIIPS4.py new file mode 100644 index 0000000..aebde31 --- /dev/null +++ b/pcucontrol/models/WTIIPS4.py @@ -0,0 +1,18 @@ +from pcucontrol.reboot import * + +class WTIIPS4(PCUControl): + supported_ports = [23] + def run_telnet(self, node_port, dryrun): + self.transport.open(self.host) + self.transport.sendPassword(self.password, "Enter Password:") + + self.transport.ifThenSend("IPS> ", "/Boot %s" % node_port) + if not dryrun: + self.transport.ifThenSend("Sure? (Y/N): ", "N") + else: + self.transport.ifThenSend("Sure? (Y/N): ", "Y") + + self.transport.ifThenSend("IPS> ", "") + + self.transport.close() + return 0 diff --git a/pcucontrol/models/X10Toggle.py b/pcucontrol/models/X10Toggle.py new file mode 100644 index 0000000..20d7cef --- /dev/null +++ b/pcucontrol/models/X10Toggle.py @@ -0,0 +1,49 @@ + +from pcucontrol.reboot import * +### 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. + +TELNET_TIMEOUT = 120 +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") + +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 diff --git a/pcucontrol/models/__init__.py b/pcucontrol/models/__init__.py new file mode 100644 index 0000000..c9b2bb1 --- /dev/null +++ b/pcucontrol/models/__init__.py @@ -0,0 +1,13 @@ +from OpenIPMI import * +from IPAL import * +from APCControl import * +from IntelAMT import * +from DRAC import * +from HPiLO import * +from BayTech import * +from WTIIPS4 import * +from ePowerSwitch import * +from BlackBoxPSMaverick import * +from CustomPCU import * +from ManualPCU import * +from PM211MIP import * diff --git a/pcucontrol/models/ePowerSwitch.py b/pcucontrol/models/ePowerSwitch.py new file mode 100644 index 0000000..7650689 --- /dev/null +++ b/pcucontrol/models/ePowerSwitch.py @@ -0,0 +1,120 @@ +from pcucontrol.reboot import * + +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_http(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() + + return 0 + +class ePowerSwitchOld(PCUControl): + def run_http(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.transport.close() + return 0 + +class ePowerSwitchOld(PCUControl): + supported_ports = [80] + def run_http(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