7

我正在尝试解决登录大量设备(数千个)的 Expect 脚本的问题。该脚本大约有 1500 行,并且相当复杂;它的工作是审核具有数千个节点的网络上的受管设备。结果,它通过 telnet 登录设备,运行命令检查设备的健康状况,将此信息记录到文件中,然后注销以继续下一个设备。

这就是我遇到问题的地方;expect我的脚本中的每个都包括一个超时和一个 eof,如下所示:

timeout {
    lappend logmsg "$rtrname timed out while <description of expect statement>"
    logmessage
    close
    wait
    set session 0
    continue
}
eof {
    lappend logmsg "$rtrname disconnected while <description of expect statement>"
    logmessage
    set session 0
    continue
}

我的 finalexpect手动关闭每个生成会话:

-re "OK.*#" {
    close
    send_user "Closing session... "
    wait
    set session 0
    send_user "closed.\n\n"
    continue
}

continue 将脚本带回启动下一个生成会话的 while 循环,假设 session = 0。

设置会话 0 跟踪生成会话何时通过超时手动关闭或在新生成会话打开之前通过 EOF 关闭,并且一切似乎都表明生成会话正在关闭,但是在大约一千个生成会话之后,我得到以下错误:

spawn telnet <IP removed>
too many programs spawned?  could not create pipe: too many open files

现在,我是一名网络工程师,而不是 UNIX 管理员或专业程序员,所以有人可以帮助我解决我的错误吗?我是否正在关闭 telnet spawn 会话但没有正确关闭频道?我编写了第二个测试脚本,它实际上只是一个接一个地连接到设备,并在连接形成后立即断开连接。它不像我的主脚本那样登录或运行任何命令,它通过数千个连接完美地工作。该脚本如下:

#!/usr/bin/expect -f

#SPAWN TELNET LIMIT TEST

set ifile [open iad.list]
set rtrname ""
set sessions 0

while {[gets $ifile rtrname] != -1} {
set timeout 2
spawn telnet $rtrname
incr sessions
send_user "Session# $sessions\n"
expect  {
    "Connected" {
                close
                wait
                continue
                }
    timeout     {
                close
                wait
                continue
                }
    eof         {
                continue
                }
}

在我的主脚本中,我正在记录每个连接以及它们可能出现 EOF 或超时的原因(通过将特定原因写入文件的 logmessage 进程),即使我只看到成功生成的连接和关闭的连接,我也得到了我的主脚本有同样的问题,但测试脚本没有。

我一直在阅读有关终止进程 ID 的内容,但据我了解,close应该是终止当前生成会话的进程 ID,而wait应该是暂停脚本,直到进程终止。我还尝试使用设备中的简单“退出”命令来关闭远程登录连接,但这并没有产生更好的结果。

我可能只需要关于如何更好地跟踪我的会话的打开和关闭并确保在设备之间没有生成会话保持打开状态的建议。任何可以提供的帮助将不胜感激。

谢谢!

4

1 回答 1

8

错误?

spawn telnet 产生了太多的程序?无法创建管道:打开的文件太多

此错误可能是由于您的系统用尽了文件句柄(或至少耗尽了您可用的计数)。

我怀疑其原因是被遗弃的 telnet 会话保持打开状态。

现在让我们谈谈为什么他们可能仍然在附近闲逛。


差远了?

关闭可能不会真正关闭 telnet 连接,特别是如果 telnet 无法识别会话已关闭,则仅期望与 telnet 的会话(请参阅:关闭命令)。在这种情况下,Telnet 很可能保持活动状态,等待来自网络端的更多输入和 TCP 保持活动。

并非所有应用程序都能识别关闭,它作为 EOF 呈现给接收应用程序。因此,即使输入已关闭,它们也可能保持打开状态。

告诉“Telnet”,结束。

在这种情况下,您将需要中断 telnet。如果您的意图是完成一些工作并退出。那么这正是我们需要做的。

对于“telnet”,您可以通过发出“send “35\r””(如果您必须自己键入键盘上的“ctrl+]”)然后是“quit”然后回车来干净地退出。这将告诉 telnet 优雅地退出。

期望脚本:启动 telnet,运行命令,关闭 telnet 摘录:

#!/usr/bin/expect
set timeout 1
set ip [lindex $argv 0]
set port [lindex $argv 1]
set username [lindex $argv 2]
set password [lindex $argv 3]
spawn telnet $ip $port
expect “‘^]’.”
send – – “\r”
expect “username:” {
    send – – “$username\r”
    expect “password:”
    send – – “$password\r”
}
expect “$”
send – – “ls\r”
expect “$”
sleep 2
# Send special ^] to telnet so we can tell telnet to quit.
send “35\r”
expect “telnet>”
# Tell Telnet to quit.
send – – “quit\r”
expect eof
# You should also, either call "wait" (block) for process to exit or "wait -nowait" (don't block waiting) for process exit.
wait

等待,为完成。

Expect - 等待命令

如果没有“等待”,expect 可能会过早地切断与进程的连接,这在极少数情况下会导致创建僵尸。如果应用程序没有更早地收到我们的信号(来自关闭的 EOF),或者如果进程没有将 EOF 解释为退出状态,那么它也可能继续运行并且您的脚本不会更明智。通过等待,我们确保我们不会忘记该过程,直到它清理并退出。

否则,在期望退出之前,我们可能不会关闭任何这些进程。如果长时间运行的期望脚本(或连接到许多服务器的脚本)没有关闭文件句柄,这可能会导致我们用完文件句柄。一旦我们用完了文件句柄,expect 和它开始的所有东西都会死掉,你不会再看到这些文件句柄用尽了。

超时?,抓住所有?,为什么?

您可能还需要考虑使用“超时”,以防服务器未按预期响应,以便我们可以提前退出。这对于严重滞后的服务器来说是理想的,它应该得到一些管理员的注意。

Catch all 可以帮助您的脚本处理任何未必会阻止我们继续的意外响应。我们可以选择继续处理,也可以选择提前退出。

期望示例摘录:

expect {           
    "password:" {
        send "password\r"
    } "yes/no)?" {
        send "yes\r"
        set timeout -1
    } timeout {
        exit
    # Below is our catch all
    } -re . {
        exp_continue
    #
    } eof {
        exit
    }
}
于 2016-12-30T17:23:54.733 回答