Problem with pexpect and 'chained' function calls
The class below is designed to manipulate a cisco-like device interface, for the purpose of executing commands and updating configuration elements.
As is currently stands, I can instantiate the class, make a call to the ssh_to_aos_expsh
function and get back valid output (e.g. get the config when the command is 'show running-config'). However, when I make a call to the ssh_to_aos_config
function (which calls the ssh_to_aos_expsh
function), I get a pexpect timeout error.
I've compared the pexpect object (the 'child' in the _ssh_connect
, ssh_to_aos_expsh
and ssh_to_aos_config
) returned by _ssh_connect
to ssh_to_aos_expsh
to the object returned by ssh_to_aos_expsh
to ssh_toaos_config
and it appears to be at the same memory location, so I'm not clear why I can't continue to manipulate the object with pexpect.
I'm not the most sophisticated python coder, so it's possible I've made some inadvertent mistake whilst trying to pass the pexpect object between functions, and if so, I would appreciate someone pointing out my mistake.
#!/usr/bin/env python
import os
import traceback
import pexpect
class SSHTool():
def __init__(self):
self.aos_user = 'some_user'
self.aos_passwd = 'some_passwd'
self.aos_init_prompt = 'accelerator>'
self.aos_enable_prompt = 'accelerator#'
self.aos_lnxsh_prompt = 'ACC#'
self.linux_passwd = 'linux_passwd'
self.root_pr开发者_运维技巧ompt = ''
def _timeout_error(self, child):
print 'SSH could not login. Timeout error.'
print child.before, child.after
return None
def _password_error(self, child):
print 'SSH could not login. Password error.'
print child.before, child.after
return None
def _ssh_connect(self, user, address, passwd):
self.root_prompt = "root@%s's password: " % address
ssh_newkey = "Are you sure you want to continue connecting"
child = pexpect.spawn('ssh -l %s %s' % (user, address))
i = child.expect([pexpect.TIMEOUT, \
ssh_newkey, \
'Password: ', \
self.root_prompt])
if i == 0: # Timeout
return self._timeout_error(child)
elif i == 1: # SSH does not have the public key. Just accept it.
child.sendline ('yes')
i = child.expect([pexpect.TIMEOUT, \
'Password: ', \
self.root_prompt])
if i == 0: # Timeout
return self._timeout_error(child)
else:
child.sendline(passwd)
return child
elif i == 2 or i == 3:
child.sendline(passwd)
return child
else:
return self._password_error(child)
def ssh_to_aos_expsh(self, ip_address, command = ''):
child = self._ssh_connect(self.aos_user, \
ip_address, \
self.aos_passwd)
i = child.expect([pexpect.TIMEOUT, \
self.aos_init_prompt])
if i == 0:
return self._timeout_error(child)
child.sendline('enable')
i = child.expect([pexpect.TIMEOUT, \
self.aos_enable_prompt])
if i == 0:
return self._timeout_error(child)
if command:
child.sendline(command)
i = child.expect([pexpect.TIMEOUT, \
self.aos_enable_prompt])
if i == 0:
return self._timeout_error(child)
else:
return child.before
else:
return child
def ssh_to_aos_config(self, ip_address, command):
child = self.ssh_to_aos_expsh(ip_address)
i = child.expect([pexpect.TIMEOUT, \
self.aos_enable_prompt])
if i == 0:
return self._timeout_error(child)
child.sendline('config')
i = child.expect([pexpect.TIMEOUT, \
self.aos_config_prompt])
if i == 0:
return self._timeout_error(child)
child.sendline(command)
i = child.expect([pexpect.TIMEOUT, \
self.aos_config_prompt])
if i == 0:
return self._timeout_error(child)
else:
return child.before
It turns out that there were two problems, both easy to fix once I knew what the issues were. First, the __init__
method contains no self.aos_config_prompt
- something which the pexpect exception stated pretty clearly when I commented out my exception handling code. Second, given a self.aos_config_prompt
that looked like 'accelerator(config)#', pexpect compiles that into re module matching code, which will only then match a prompt containing the contents of the parentheses. Simply escape the parentheses in the string and the match works as desired.
I would guess that the timeout happens because ssh_to_aos_config()
does not get all the input it expects: the call to ssh_to_aos_expsh()
might well work, while subsequent calls to expect
would not.
So a question would be: where does the timeout happen? You could track this by raising an exception instead of returning self._timeout_error(child). The location you find would point to an input that pexpect never gets (hence the timeout), and you could update your code there.
If you are getting a timeout it is because you are not getting any of the strings you are expecting. It may be that you are getting an error message instead, or the prompt you are expecting is wrong.
Enable logging to see the whole interaction - in pexpect 2.3 this is done by assigning a file object to the child.logfile attribute - then you can see exactly what is happening. Check the docs for earlier versions, since I think this has changed.
I notice a couple of things in your code:
1) the root_prompt is an empty string. This will always match immediately, even if nothing has been returned from the client. This may be the cause of your problem - the ssh connect function thinks it has seen the prompt and successfully logged in, while the client is still waiting for some other input.
2) there is a syntax error in your code - in ssh_connect you have the sequence:
if i == 0: # Timeout
return self._timeout_error(child)
else:
child.sendline(passwd)
return child
elif i == 2 or i == 3:
child.sendline(passwd)
return child
else:
return self._password_error(child)
The elif does not match up with an if statement, so AFAIK this would never compile. I presume it is a cut & paste error, since you say you have been running the code.
精彩评论