PCU Class objects broken out into individual files.
authorStephen Soltesz <soltesz@cs.princeton.edu>
Tue, 6 Jan 2009 19:22:26 +0000 (19:22 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Tue, 6 Jan 2009 19:22:26 +0000 (19:22 +0000)
15 files changed:
pcucontrol/models/APCControl.py [new file with mode: 0644]
pcucontrol/models/BayTech.py [new file with mode: 0644]
pcucontrol/models/BlackBoxPSMaverick.py [new file with mode: 0644]
pcucontrol/models/CustomPCU.py [new file with mode: 0644]
pcucontrol/models/DRAC.py [new file with mode: 0644]
pcucontrol/models/HPiLO.py [new file with mode: 0644]
pcucontrol/models/IPAL.py [new file with mode: 0644]
pcucontrol/models/IntelAMT.py [new file with mode: 0644]
pcucontrol/models/ManualPCU.py [new file with mode: 0644]
pcucontrol/models/OpenIPMI.py [new file with mode: 0644]
pcucontrol/models/PM211MIP.py [new file with mode: 0644]
pcucontrol/models/WTIIPS4.py [new file with mode: 0644]
pcucontrol/models/X10Toggle.py [new file with mode: 0644]
pcucontrol/models/__init__.py [new file with mode: 0644]
pcucontrol/models/ePowerSwitch.py [new file with mode: 0644]

diff --git a/pcucontrol/models/APCControl.py b/pcucontrol/models/APCControl.py
new file mode 100644 (file)
index 0000000..62f5f6f
--- /dev/null
@@ -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 <ENTER> to cancel", 
+                                                       "YES\r\n",
+                                                       ExceptionSequence)
+               else:
+                       self.transport.ifThenSend("Enter 'YES' to continue or <ENTER> to cancel", 
+                                                       "", ExceptionSequence)
+               self.transport.ifThenSend("Press <ENTER> 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/&nbsp;//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/&nbsp;//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/&nbsp;//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 (file)
index 0000000..83de3a5
--- /dev/null
@@ -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 (file)
index 0000000..ee414dc
--- /dev/null
@@ -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<port>=r'
+# relies on basic authentication within http1.0
+# first curl-based script was
+# curl --http1.0 --basic --user <username>:<password> --data P<port>=r \
+#      http://<hostname>:<http_port>/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 (file)
index 0000000..60de348
--- /dev/null
@@ -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 (file)
index 0000000..a2731cd
--- /dev/null
@@ -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 (file)
index 0000000..25d4331
--- /dev/null
@@ -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("</system1>hpiLO->", "POWER")
+               else:
+                       # Reset this machine
+                       self.transport.ifThenSend("</system1>hpiLO->", "reset")
+
+               self.transport.ifThenSend("</system1>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 (file)
index 0000000..75668db
--- /dev/null
@@ -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 (file)
index 0000000..af201ba
--- /dev/null
@@ -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 (file)
index 0000000..3a21cd9
--- /dev/null
@@ -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 (file)
index 0000000..f52ea39
--- /dev/null
@@ -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 (file)
index 0000000..007b307
--- /dev/null
@@ -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 (file)
index 0000000..aebde31
--- /dev/null
@@ -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 (file)
index 0000000..20d7cef
--- /dev/null
@@ -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 (file)
index 0000000..c9b2bb1
--- /dev/null
@@ -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 (file)
index 0000000..7650689
--- /dev/null
@@ -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