Add expect version of APC controller
authorStephen Soltesz <soltesz@cs.princeton.edu>
Sun, 11 Dec 2011 10:51:24 +0000 (10:51 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Sun, 11 Dec 2011 10:51:24 +0000 (10:51 +0000)
Remove debug comments from util.command
Fix bug in DRAC.exp by separating command line arguments

pcucontrol/models/APCControl.py
pcucontrol/models/exp/APC.exp [new file with mode: 0755]
pcucontrol/models/exp/DRAC.exp
pcucontrol/util/command.py

index ebe43e2..52dfe18 100644 (file)
 from pcucontrol.reboot import *
 import subprocess
 
-class APCControl(PCUControl):
-       supported_ports = [22,23,80,443]
-       reboot_sequence = []
-
-       def run(self, node_port, dryrun):
-               print "RUNNING!!!!!!!!!!!!"
-               if self.transport.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.transport.type == Transport.HTTPS:
-                               cmd = self.get_https_cmd()
-                       elif self.transport.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 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
-
-               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.transport.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.transport.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 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
-               result = p.read()
-               return result.strip()
-
-       def logout(self):
-               # NOTE: log out again, to allow other uses to access the machine.
-               if self.transport.type == Transport.HTTP:
-                       cmd = """curl -s --anyauth --user '%s:%s' http://%s/logout.htm """ + \
-                                 """ | grep -E '^[^<]+' """
-               elif self.transport.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 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
-               print p.read()
+class APCControl(BasicPCUControl):
+    supported_ports = [22,23,80,443]
+#    reboot_sequence = []
+
+    def run(self, node_port, dryrun):
+        print "RUNNING!!!!!!!!!!!!"
+        if self.transport.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 pcu_run(self, node_port, dryrun=False):
+        return self.run_telnet_or_ssh(node_port, dryrun)
+
+    def pcu_test(self, node_port, dryrun=True):
+        return self.run_telnet_or_ssh(node_port, dryrun)
+    
+    def run_telnet_or_ssh(self, node_port, dryrun):
+        r = self.run_telnet(node_port, dryrun)
+        if "No error" in r:
+            return r
+        r2 = self.run_ssh(node_port, dryrun)
+        if "No error" in r2:
+            return r2
+        return r + " :: " +r2
+
+    def run_ssh(self, node_port, dryrun):
+        return self.run_expect_script("APC.exp ssh", dryrun=dryrun, model="None", sequence=self.reboot_sequence)
+    def run_telnet(self, node_port, dryrun):
+        return self.run_expect_script("APC.exp telnet", dryrun=dryrun, model="None", sequence=self.reboot_sequence)
+
+
+    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.transport.type == Transport.HTTPS:
+                cmd = self.get_https_cmd()
+            elif self.transport.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 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
+
+        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.transport.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.transport.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 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
+        result = p.read()
+        return result.strip()
+
+    def logout(self):
+        # NOTE: log out again, to allow other uses to access the machine.
+        if self.transport.type == Transport.HTTP:
+            cmd = """curl -s --anyauth --user '%s:%s' http://%s/logout.htm """ + \
+                  """ | grep -E '^[^<]+' """
+        elif self.transport.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 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
+        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)
+    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)
+    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)
+    def run_telnet_or_ssh(self, node_port, dryrun):
+        print "TEST! "
+        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)
+    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)
+    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/exp/APC.exp b/pcucontrol/models/exp/APC.exp
new file mode 100755 (executable)
index 0000000..81d08ac
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/expect
+
+set timeout 60
+set method [lindex $argv 0]
+set host [lindex $argv 1]
+set user [lindex $argv 2]
+set password [lindex $argv 3]
+set dryrun [lindex $argv 4]
+set model [lindex $argv 5]
+set sequence [lindex $argv 6]
+
+log_user 0
+if { "$dryrun" == "True" } {
+    set reset_msg "Test: No error"
+} else {
+    set reset_msg "Reset: No error"
+}
+
+set cmd_list { {"ssh $user@$host"} {"telnet $host"} }
+
+if { "$method" == "ssh" || "$method" == "telnet" } {
+
+    set env(TERM) "vt100"
+    if { "$method" == "telnet" } {
+        spawn telnet $host
+    } else { 
+        spawn ssh $user@$host
+    }
+
+    expect {
+        "password:" { send "$password\n" ; exp_continue }
+        -re "User Name.*" { send "$user\r"; exp_continue}
+        -re "Password.*" { send "$password\r"; exp_continue}
+        -re "Access to the Control Console will be denied" { 
+                    send_user "Error $method: $expect_out(0,string)\n"; exit } 
+        "Are you sure you want to continue connecting (yes/no)? " { send "yes\n" ; exp_continue }
+        "Permission denied" { send_user "Error $method: username/password: $expect_out(0,string)"; exit }
+        "Could not resolve hostname" { send_user "Error $method: $expect_out(0,string)"; exit }
+        "Connection refused" { send_user "Error $method: Could not connect\n"; exit }
+        "Connection timed out" { send_user "Error $method: Connection timed out\n"; exit }
+        "No route to host" { send_user "Error $method: $expect_out(0,string)"; exit }
+        "> " { send "\r\n"; } 
+        timeout { send_user "Error $method: Timeout\n"; exit; }
+    }
+}
+
+
+set steps [split $sequence " "]
+foreach step $steps {
+    #send_user "Sending step $step\n";
+    expect "> " { send "$step\r\n"; }
+}
+
+if { "$dryrun" == "True" } {
+    expect "Enter 'YES' to continue or <ENTER> to cancel" { send "\r\n\r\n"; } 
+} else {
+    expect "Enter 'YES' to continue or <ENTER> to cancel" { send "YES\r\n"; } 
+}
+expect "> " { send "\e\e\e\e4\r\n"; } 
+
+expect eof { send_user "$reset_msg\n" }
index fc83df7..57b5e46 100755 (executable)
@@ -62,9 +62,11 @@ if { "$method" == "ssh" } {
 } else { 
     if { [string match "*racadm*" $method] } {
         if { "$dryrun" == "True" } {
-            set rac_cmd "getsysinfo"
+            set rac_cmd_a "getsysinfo"
+            set rac_cmd_b ""
         } else {
-            set rac_cmd "serveraction powercycle"
+            set rac_cmd_a "serveraction"
+            set rac_cmd_b "powercycle"
         }
         #send_user "/opt/dell/srvadmin/bin/$method -r $host -u $user -p '$password' $rac_cmd\n";
         set cont 0
@@ -74,10 +76,20 @@ if { "$method" == "ssh" } {
             #       on the cmdline causes racadm to fail.
             # NOTE: For unknown reason, some DRAC modules work only with
             #       stderr redirected to stdout, others only work without it.
+            # NOTE: For unknown reason, the racadm cmd does not recognize "serveraction powercycle" 
+            #       but does recognize "serveraction" "powercycle"
             if { $retry == 0 } {
-                set x [ spawn /opt/dell/srvadmin/rac5/bin/$method -i -r $host $rac_cmd 2>&1 ]
+               if { "$rac_cmd_a" == "getsysinfo" } {
+                   set x [ spawn /opt/dell/srvadmin/rac5/bin/$method -i -r $host $rac_cmd_a ]
+               } else {
+                   set x [ spawn /opt/dell/srvadmin/rac5/bin/$method -i -r $host $rac_cmd_a $rac_cmd_b  ]
+               }
             } else {
-                set x [ spawn /opt/dell/srvadmin/rac5/bin/$method -i -r $host $rac_cmd  ]
+               if { "$rac_cmd_a" == "getsysinfo" } {
+                   set x [ spawn /opt/dell/srvadmin/rac5/bin/$method -i -r $host $rac_cmd_a 2>&1 ]
+               } else {
+                   set x [ spawn /opt/dell/srvadmin/rac5/bin/$method -i -r $host $rac_cmd_a $rac_cmd_b 2>&1 ]
+               }
             }
 
             expect {
@@ -92,10 +104,11 @@ if { "$method" == "ssh" } {
                 -re "Authentication failed.*" { send_user "DRAC: $expect_out(0,string)"; exit }
                 -re "This.*not support remote RACADM" { send_user "DRAC: $expect_out(0,string)" ; exit }
                 -re "ERROR: The syntax of the command specified is not correct." { set cont 1 }
+                -re "ERROR: the command that was entered is not valid" { set cont 1 }
                 -re "INVALID ARGUMENT" { send_user "DRAC: received 'INVALID ARGUMENT'\n"; exit }
                 -re "RAC Information:" { sleep .1; }
                 timeout { send_user "DRAC: timeout\n" ; exit }
-                eof { send_user "DRAC: early EOF\n"; exit ; }
+                eof { send_user "$reset_msg\n"; exit ; }
                 #-re "ERROR:.*" { send_user "DRAC: $expect_out(0,string)"; exit }
             }
             if { $cont == 0 } { break; }
index 74b5fae..173255b 100644 (file)
@@ -36,8 +36,8 @@ def read_t(stream, count=1, timeout=COMMAND_TIMEOUT*2):
                while True:
                        lin, lout, lerr = select([stream], [], [], timeout)
                        if len(lin) == 0:
-                               print "timeout!"
-                               raise ExceptionReadTimeout("TIMEOUT reading from command")
+                               #print "timeout!"
+                               raise ExceptionReadTimeout("TIMEOUT while reading from command")
 
                        try:
                                outbytes = stream.read(count)
@@ -55,7 +55,7 @@ def read_t(stream, count=1, timeout=COMMAND_TIMEOUT*2):
        else:
                lin, lout, lerr = select([stream], [], [], timeout)
                if len(lin) == 0:
-                       raise ExceptionReadTimeout("TIMEOUT reading from command")
+                       raise ExceptionReadTimeout("TIMEOUT while reading from command")
 
                return stream.read(count)
 
@@ -68,10 +68,10 @@ class CMD:
                try:
                        return CMD.run(self,cmd,timeout)
                except ExceptionTimeout:
-                       print traceback.print_exc()
+                       #print traceback.print_exc()
                        return ("", "ScriptTimeout")
                except ExceptionReadTimeout:
-                       print traceback.print_exc()
+                       #print traceback.print_exc()
                        return ("", "RunningScriptTimeout")
                except KeyboardInterrupt:
                        print "Interrupted, exiting..."
@@ -101,7 +101,7 @@ class CMD:
                lout, lin, lerr = select([f_out], [], [f_err], timeout)
                if len(lin) == 0 and len(lout) == 0 and len(lerr) == 0:
                        # Reached a timeout!  Nuke process so it does not hang.
-                       print "TIMEOUT!!!!!!!!!!!!!!!!!!!"
+                       #print "TIMEOUT!!!!!!!!!!!!!!!!!!!"
                        s.kill(signal.SIGKILL)
                        raise ExceptionTimeout("TIMEOUT Running: %s" % cmd)
                else: