2014-11-21 34 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