2014-11-21 33 views
2

tl; dr;如何从脚本中捕获stderr以获取更具体的错误,而不仅仅依赖Net :: OpenSSH中的一般错误?如何使用perl和Net :: OpenSSH检测远端是否只处理协议1?

我有一个棘手的问题,我试图解决。 Net :: OpenSSH只适用于协议版本2.但是,我们有一些网络设备只支持版本1.我试图找到一种检测远端是否错误版本的优雅方法。

当连接到版本1的装置,下面的消息显示出来的标准错误

协议主要版本不同:2对比1

然而由净返回错误: :OpenSSH如下

无法建立主SSH连接:密码错误或主进程意外退出

这个特别的错误太笼统了,不能解决协议版本的差异。我需要通过切换到另一个库来处理协议差异,我不想为每个连接错误都这样做。

我们使用了一个相当复杂的流程,最初仅用于telnet访问。我们加载一个“comm”对象,然后确定像路由器类型等东西。该comm对象调用Net :: OpenSSH来传递命令。

例子:

my $sshHandle = eval { $commsObject->go($router) }; 
my $sshError = $sshHandle->{ssh}->error; 

if ($sshError) { 
    $sshHandle->{connect_error} = $sshError; 
    return $sshHandle; 
} 

凡协议错误显示出来的标准错误是在这里

$args->{ssh} = eval { 
     Net::OpenSSH->new(
      $args->{node_name}, 
      user  => $args->{user}, 
      password => $args->{tacacs}, 
      timeout  => $timeout, 
      master_opts => [ -o => "StrictHostKeyChecking=no" ] 
     ); 
    }; 

我想什么做的是通过在标准错误协议错误,而不是传回的一般错误由Net :: OpenSSH。我想在脚本中这样做,但我不知道如何从脚本中捕获stderr。

任何想法,将不胜感激。

回答

2

捕获主stderr流,然后检查它。

请参阅here该怎么做。

您可以使用的另一种方法是打开到远程SSH服务器的套接字。它发回的第一件事是它的版本字符串。例如:

$ nc localhost 22 
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-8 
^C 

从这些信息你应该能够推断出服务器是否支持SSH v2。最后,如果您还需要与SSH v1服务器交谈,我的其他模块Net::SSH::Any的开发版本可以使用操作系统本机SSH客户端来完成此操作,不过它为每个命令都建立了一个新的SSH连接。

use Net::SSH::Any; 

my $ssh = Net::SSH::Any->new($args->{node_name}, 
          user  => $args->{user}, 
          password => $args->{tacacs}, 
          timeout  => $timeout, 
          backends => 'SSH_Cmd', 
          strict_host_key_checking => 0); 

更新:在回答下面比尔的评论在同一会话发送多个命令的问题:

在同一会话发送命令的问题是,你必须先跟远程shell,并且没有办法以通用的方式可靠地做到这一点,因为每个shell都以不同的方式做事,特别是对于自动化不友好的网络设备shell。

无论如何,CPAN上有几个模块试图做到这一点,为每种外壳(或操作系统)实现一个处理程序。例如,检查Oliver Gorwits的模块Net::CLI::Interact,Net::Appliance::SessionNet::Appliance::Phrasebook。短语手法似乎非常合适。

+0

有趣的想法。我真的不想处理写入文件的开销,但第二种方法很有趣。我可能只是写一个探测器来处理检测。在第三个选项中,Net :: OpenSSH为每个命令打开一个新的会话。我不得不用telnet包装这个命令来解决这个问题(参考我之前的一些问题)。如果你可以开发一个不需要使用telnet发送多个命令的ssh库,那将会增加很多价值。 – Bill 2014-11-21 21:44:16

+0

@ Bill:我已经更新了我的答案 – salva 2014-11-22 07:49:09