From dd516e0f7a0f19fbc20204be8acca09687919db9 Mon Sep 17 00:00:00 2001 From: Stephen Soltesz Date: Sun, 11 Dec 2011 10:51:24 +0000 Subject: [PATCH] Add expect version of APC controller Remove debug comments from util.command Fix bug in DRAC.exp by separating command line arguments --- pcucontrol/models/APCControl.py | 325 ++++++++++++++++---------------- pcucontrol/models/exp/APC.exp | 61 ++++++ pcucontrol/models/exp/DRAC.exp | 23 ++- pcucontrol/util/command.py | 12 +- 4 files changed, 244 insertions(+), 177 deletions(-) create mode 100755 pcucontrol/models/exp/APC.exp diff --git a/pcucontrol/models/APCControl.py b/pcucontrol/models/APCControl.py index ebe43e2..52dfe18 100644 --- a/pcucontrol/models/APCControl.py +++ b/pcucontrol/models/APCControl.py @@ -1,179 +1,172 @@ 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 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.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/ //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/ //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/ //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/ //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/ //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/ //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 index 0000000..81d08ac --- /dev/null +++ b/pcucontrol/models/exp/APC.exp @@ -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 to cancel" { send "\r\n\r\n"; } +} else { + expect "Enter 'YES' to continue or to cancel" { send "YES\r\n"; } +} +expect "> " { send "4\r\n"; } + +expect eof { send_user "$reset_msg\n" } diff --git a/pcucontrol/models/exp/DRAC.exp b/pcucontrol/models/exp/DRAC.exp index fc83df7..57b5e46 100755 --- a/pcucontrol/models/exp/DRAC.exp +++ b/pcucontrol/models/exp/DRAC.exp @@ -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; } diff --git a/pcucontrol/util/command.py b/pcucontrol/util/command.py index 74b5fae..173255b 100644 --- a/pcucontrol/util/command.py +++ b/pcucontrol/util/command.py @@ -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: -- 2.43.0