程式扎記: [ Python 常見問題 ] Python pexpect - TIMEOUT falls into traceback and exists

標籤

2015年11月18日 星期三

[ Python 常見問題 ] Python pexpect - TIMEOUT falls into traceback and exists

Source From Here
Question
I'm new to python-pexpect. In Tcl/expect when I hit a timeout - I would respond with message and exit the function. I have tried to experiment with similar response using sample code posted as Below:
- test.py
  1. #!/usr/bin/env python  
  2.   
  3. """This runs 'ls -l' on a remote host using SSH. At the prompts enter hostname,  
  4. user, and password.  
  5.   
  6. $Id$  
  7. """  
  8.   
  9. import pexpect  
  10. import getpass, os  
  11.   
  12. def ssh_command (user, host, password, command):  
  13.   
  14.     """This runs a command on the remote host. This could also be done with the  
  15. pxssh class, but this demonstrates what that class does at a simpler level.  
  16. This returns a pexpect.spawn object. This handles the case when you try to  
  17. connect to a new host and ssh asks you if you want to accept the public key  
  18. fingerprint and continue connecting. """  
  19.   
  20.     ssh_newkey = 'Are you sure you want to continue connecting'  
  21.     child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command))  
  22.     i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])  
  23.     if i == 0: # Timeout  
  24.         print 'ERROR!'  
  25.         print 'SSH could not login. Here is what SSH said:'  
  26.         print child.before, child.after  
  27.         return None  
  28.     if i == 1: # SSH does not have the public key. Just accept it.  
  29.         child.sendline ('yes')  
  30.         child.expect ('password: ')  
  31.         i = child.expect([pexpect.TIMEOUT, 'password: '])  
  32.         if i == 0: # Timeout  
  33.             print 'ERROR!'  
  34.             print 'SSH could not login. Here is what SSH said:'  
  35.             print child.before, child.after  
  36.             return None         
  37.     child.sendline(password)  
  38.     return child  
  39.   
  40. def main ():  
  41.   
  42.     host = raw_input('Hostname: ')  
  43.     user = raw_input('User: ')  
  44.     password = getpass.getpass('Password: ')  
  45.     child = ssh_command (user, host, password, '/bin/ls -l')  
  46.     child.expect(pexpect.EOF)  
  47.     print child.before  
  48.   
  49. if __name__ == '__main__':  
  50.     try:  
  51.         main()  
  52.     except Exception, e:  
  53.         print str(e)  
  54.         traceback.print_exc()  
  55.         os._exit(1)  
I based on this code above - if I give a bogus password, I would expect this to just timeout, print "ERROR!", and exit program. But when I run it - goes into a 'Traceback output , can someone help me to get the program to print "ERROR" and exit program gracefully.

How-To
First off, you should import traceback, so if there is an unexpected error in the program, traceback.print_exc() will work.
  1. import getpass, os, traceback  
I also created a die() function so that ssh gets closed when the program quits.
  1. def die(child, errstr):  
  2.     print errstr  
  3.     print child.before, child.after  
  4.     child.terminate()  
  5.     exit(1)  
Delete this line, since it was probably left there by mistake after adding the correct command on the next line:
  1. child.expect ('password: ')  
The correct line (which handles timeouts) is:
  1. i = child.expect([pexpect.TIMEOUT, 'password: '])  
then replace this:
  1. child.expect(pexpect.EOF)  
  2. print child.before  
with this:
  1. i = child.expect([pexpect.TIMEOUT, 'Permission denied', pexpect.EOF])  
  2. if i == 0:  
  3.     die(child, 'ERROR!\nSSH timed out. Here is what SSH said:')  
  4. elif i == 1:  
  5.     die(child, 'ERROR!\nIncorrect password Here is what SSH said:')  
  6. elif i == 2:  
  7.     print child.before  
This change will tell the program to detect a bad password, or to timeout gracefully if there is unknown input, instead of raising an exception. Note that if your command runs longer than the default timeout duration, it will be aborted before it finishes. You'll have to set 'timeout' as you call expect() in order to fix that.
Also returning 'None' after a timeout isn't useful. Instead, call die() which will call exit(1):
  1. die(child, 'error message')  
This is the final code:
  1. #!/usr/bin/env python  
  2.   
  3. """This runs 'ls -l' on a remote host using SSH. At the prompts enter hostname,  
  4. user, and password.  
  5.   
  6. $Id$  
  7. """  
  8.   
  9. import pexpect  
  10. import getpass, os, traceback  
  11.   
  12. def ssh_command (user, host, password, command):  
  13.   
  14.     """This runs a command on the remote host. This could also be done with the  
  15. pxssh class, but this demonstrates what that class does at a simpler level.  
  16. This returns a pexpect.spawn object. This handles the case when you try to  
  17. connect to a new host and ssh asks you if you want to accept the public key  
  18. fingerprint and continue connecting. """  
  19.   
  20.     ssh_newkey = 'Are you sure you want to continue connecting'  
  21.     child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command))  
  22.     i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])  
  23.     if i == 0: # Timeout  
  24.         die(child, 'ERROR!\nSSH could not login. Here is what SSH said:')  
  25.     if i == 1: # SSH does not have the public key. Just accept it.  
  26.         child.sendline ('yes')  
  27.         i = child.expect([pexpect.TIMEOUT, 'password: '])  
  28.         if i == 0: # Timeout  
  29.             die(child, 'ERROR!\nSSH could not login. Here is what SSH said:')  
  30.     child.sendline(password)  
  31.     return child  
  32.   
  33. def die(child, errstr):  
  34.     print errstr  
  35.     print child.before, child.after  
  36.     child.terminate()  
  37.     exit(1)  
  38.   
  39. def main ():  
  40.   
  41.     host = raw_input('Hostname: ')  
  42.     user = raw_input('User: ')  
  43.     password = getpass.getpass('Password: ')  
  44.     child = ssh_command (user, host, password, '/bin/ls -l')  
  45.   
  46.     i = child.expect([pexpect.TIMEOUT, 'Permission denied', pexpect.EOF])  
  47.     if i == 0:  
  48.         die(child, 'ERROR!\nSSH timed out. Here is what SSH said:')  
  49.     elif i == 1:  
  50.         die(child, 'ERROR!\nIncorrect password Here is what SSH said:')  
  51.     elif i == 2:  
  52.         print child.before  
  53.   
  54. if __name__ == '__main__':  
  55.     try:  
  56.         main()  
  57.     except Exception, e:  
  58.         print str(e)  
  59.         traceback.print_exc()  
  60.         os._exit(1)  


沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!