简体   繁体   English

pexpect和“链接”函数调用的问题

[英]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. 下面的类旨在操纵类cisco的设备接口,以执行命令和更新配置元素。

As is currently stands, I can instantiate the class, make a call to the ssh_to_aos_expsh function and get back valid output (eg get the config when the command is 'show running-config'). 按照目前的情况,我可以实例化该类,调用ssh_to_aos_expsh函数并返回有效输出(例如,当命令为“ 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. 但是,当我调用ssh_to_aos_config函数(该函数调用ssh_to_aos_expsh函数)时,出现了预期超时错误。

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. 我将_ssh_connect返回_ssh_connectssh_to_aos_expsh返回的pexpect对象( _ssh_connectssh_to_aos_expshssh_to_aos_config的“子”)与ssh_to_aos_config返回到ssh_to_aos_expsh的位置的ssh_to_aos_expsh进行了ssh_toaos_config ,因此看起来不一样为什么我不能继续用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. 我不是最复杂的python编码器,因此在尝试在函数之间传递pexpect对象时,我可能犯了一些无意的错误,如果是这样,我会感谢有人指出我的错误。

#!/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_prompt = ''

    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. 首先, __init__方法不包含self.aos_config_prompt当我注释掉异常处理代码时,pexpect异常非常清楚地说明了这一点。 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. 第二,给定一个self.aos_config_prompt看起来像“促进剂(配置)#”,Pexpect的编译到这一点re模块匹配的代码,然后只将匹配包含括号的内容的提示。 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. 我猜想发生超时是因为ssh_to_aos_config()没有得到它期望的所有输入: ssh_to_aos_expsh()的调用可能会正常工作,而随后的expect调用则不会。

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). 您可以通过引发异常而不是返回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. 启用日志记录以查看整个交互-在pexpect 2.3中,这是通过将文件对象分配给child.logfile属性来完成的-然后您可以确切地看到正在发生的事情。 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. 1)root_prompt是一个空字符串。 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. 这可能是造成问题的原因-ssh connect函数认为它已经看到了提示并成功登录,而客户端仍在等待其他输入。

2) there is a syntax error in your code - in ssh_connect you have the sequence: 2)您的代码中存在语法错误-在ssh_connect中,您具有以下顺序:

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. Elif与if语句不匹配,因此AFAIK永远不会编译它。 I presume it is a cut & paste error, since you say you have been running the code. 我认为这是剪切和粘贴错误,因为您说您一直在运行代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM