Groovy / Expectテキストベースのインタラクション

最終更新日: 20 年 2023 月 XNUMX 日

概要

LogicMonitor CollectorのGroovyトリックのキッチンシンクの最も強力な機能のXNUMXつは、 期待ベース 新しいヘルパークラス。 多くのネットワークデバイスでは、SSH / Telnet経由で接続し、CLIでコマンドを実行する必要があります。 Expectメソッドを使用すると、コマンドを入力して出力を自分で読んでいるかのように、プロセスやデバイスとリモートで対話するGroovyスクリプトを作成できます。

コレクターSSH通信

コレクターは、SSH接続を使用して他のデバイスやリソースと通信し、データを収集します。 一部のコアLogicModuleはJSchを直接呼び出してSSH接続を確立しますが、Groovyスクリプトで記述されたほとんどのLogicModuleは、SSH接続を確立するためにJSchライブラリまたはSSHJライブラリのいずれかを指定できるExpectフレームワークを使用します。 

Expectを使用している場合は、ライブラリをExpect.open()メソッドに渡すことができます。

Expect.open(host, user, password, "lib=jsch");
Expect.open(host, user, password, "lib=sshj");

特定のプロトコルを渡さない場合、デフォルトでJSchが使用されます。 設定することにより、コレクターのagent.confのデフォルト構成をオーバーライドできます。 ssh.preferredlibrary = sshj.

相互作用の例

次に、この機能がどのように機能するかを示すいくつかの例を示します。

SSH接続:リモートサーバー上のTomcatログファイルサイズを確認します

この最初の例では、Groovy / Expectを使用してssh経由でリモートサーバーに接続し、プロンプトからコマンドを実行し、出力を解析してデータポイント値を取得しています。

// import the logicmonitor expect helper class
import com.santaba.agent.groovyapi.expect.Expect;

// get the hostname and credentials from the device property table
hostname = hostProps.get("system.hostname");
userid = hostProps.get("ssh.user");
passwd = hostProps.get("ssh.pass");

// initiate an ssh connection to the host using the provided credentials
ssh_connection = Expect.open(hostname, userid, passwd);

// wait for the cli prompt, which indicates we've connected
ssh_connection.expect("# ");

// send a command to show the tomcat log file size, along with the newline [enter] character
ssh_connection.send("ls -l /usr/local/tomcat/logs/catalina.out\n");

// wait for the cli prompt to return, which indicates the command has completed
ssh_connection.expect("# ");

// capture all the text up to the expected string. this should look something like
// -rw-r--r-- 1 root root 330885412 Jan 11 20:40 /usr/local/tomcat/logs/catalina.out
cmd_output = ssh_connection.before();

// now that we've capture the data we care about lets exit from the cli
ssh_connection.send("exit\n");

// wait until the external process finishes then close the connection
ssh_connection.expectClose();

// now let's iterate over each line of the we collected
cmd_output.eachLine
{ line ->

    // does this line contain the characters "-rw"
    if ( line =~ /\-rw/ )
    {
        // yes -- this is the line containing the output of our ls command
        // tokenize the cmd output on one-or-more whitespace characters
        tokens = line.split(/\s+/);

        // print the 5th element in the array, which is the size
        println tokens[4];
    }
}

// return with a response code that indicates we ran successfully
return (0);

このコードをデータソースで使用するには、スクリプト出力をデータ値として使用する単一のデータポイントを作成します。

Telnet /ポート接続:FTPサーバーの可用性をテストします

この次の例では、Groovy / Expectを使用してリモートサーバーの標準ポートに接続し、ログインしてから終了します。 このようなことを行って、サービスの応答時間をテストし、特定のしきい値を超えたときにアラートを出すことができるようにします。

// import the logicmonitor expect helper class
import com.santaba.agent.groovyapi.expect.Expect;

// get the hostname and credentials from the device property table
hostname = hostProps.get("system.hostname");
userid = hostProps.get("ftp.user");
passwd = hostProps.get("ftp.pass");

// specify port 21 and a 5 second connection timeout
port = 21;
timeout = 5;

// initiate an connection to the host:port
ftp_connection = Expect.open(hostname, port, timeout);

// wait for the 220 prompt from the FTP server, then send the userid and a newline [enter] character
ftp_connection.expect("220 ");
ftp_connection.send("user " + userid + "\n");

// wait for the 331 prompt from the FTP server, then send the passwd and a newline [enter] character
ftp_connection.expect("331 ");
ftp_connection.send(passwd + "\n");

// wait for the 230 prompt from the FTP server, then send quit and a newline [enter] character
ftp_connection.expect("230 ");
ftp_connection.send("quit\n");

// return with a response code that indicates we ran successfully
return (0);

このインタラクションの応答時間を測定するには、「実行時間」タイプのデータポイントを作成します。

外部プロセスの相互作用:nslookupを使用してDNSサーバーをテストする

「nslookup」コマンドとの次の相互作用について考えてみます。このコマンドでは、DNSサーバーが8.8.4.4であることをテストしています。 「www.yahoo.com」という名前を解決できます。

Groovy / Expectで次のように同じ相互作用を実行できます。

// import the logicmonitor expect helper class
import com.santaba.agent.groovyapi.expect.Expect;

// open a handle to the nslookup process and wait for the prompt
nslookup = Expect.open("nslookup.exe");
nslookup.expect( '> ');

// send the server command to change dns source
nslookup.send("server 8.8.4.4\n");
nslookup.expect('> ');

// send the hostname we want to resolve and wait for the prompt
nslookup.send("www.yahoo.com\n");
nslookup.expect('> ');

// capture the output up to the "> " prompt, and then exit the nslookup prompt
nslookup_output = nslookup.before();
nslookup.send("exit\n");

// iterate over each line we collected from the output
nslookup_output.eachLine
{ line ->

    // does this line match "Addresses: x.x.x.x" ?
    if ( line =~ /Addresses:\s+\d+\.\d+\.\d+\.\d+/ )
    {
        // yes -- this is the line containing the address resolution information
        // tokenize the nslookup output on ': '
        tokens = line.split(/:\s+/);

        // print the address that was resolved
        println tokens[1];
    }
}

// return with a response code that indicates we ran successfully
return (0);

SSH接続:デバイス構成を取得する

ここでは、Groovy / Expectを使用してネットワークデバイスに接続し、ConfigSourceで使用するためにその構成を取得します。

// import the logicmonitor expect helper class
import com.santaba.agent.groovyapi.expect.Expect;

// get the hostname and credentials from the device property table
hostname = hostProps.get("system.hostname");
userid = hostProps.get("ssh.user");
passwd = hostProps.get("ssh.pass");

// open an ssh connection and wait for the prompt
ssh_connection = Expect.open(hostname, userid, passwd);
ssh_connection.expect(">");

// enter enable mode
ssh_connection.send("enable\n");
ssh_connection.expect(":");
ssh_connection.send(passwd + "\n")
ssh_connection.expect("#");

// ensure the page-by-page view doesn't foul the config output
ssh_connection.send("terminal pager 0\n");
ssh_connection.expect("#");

// display the config
ssh_connection.send("show running-config \n");

// logout from the device
ssh_connection.send("exit\n");
ssh_connection.expect("# exit");

// collect the output, then close the ssh connection
config=ssh_connection.before();
ssh_connection.expectClose();

// strip the "ntp clock-period" value and print the config
config = config.replaceAll(/ntp clock-period \d+/,"ntp clock-period ")
println config;

// return with a response code that indicates we ran successfully
return(0);

メソッドリファレンスを期待する

オブジェクトのインスタンス化

Expect.open(host、user、passwd、[timeout])– SSH接続オブジェクトをリモートCLIにインスタンス化します

  • @param文字列 host –ssh接続を開始するホスト名
  • @param文字列 user –ユーザー名の資格情報
  • @param文字列 passwd –パスワード資格情報
  • @param 整数 タイムアウト –秒単位の接続タイムアウト
  • @returnオブジェクト 接続 後続のメソッド呼び出しで使用するexpectオブジェクト

Expect.open(host、port、 [タイムアウト]–TCP接続オブジェクトをリモートCLIにインスタンス化します

  • @param文字列 host –接続するホスト名
  • @param 整数 ポート –接続する必要があるtcpポート
  • @param 整数 タイムアウト –秒単位の接続タイムアウト
  • @returnオブジェクト 接続 後続のメソッド呼び出しで使用するexpectオブジェクト

Expect.open(コマンド、[タイムアウト]) –シェルコマンド接続オブジェクトをインスタンス化します

  • @param文字列 command –実行するコマンドのフルパス
  • @param 整数 タイムアウト –秒単位の接続タイムアウト
  • @returnオブジェクト 接続 後続のメソッド呼び出しで使用するexpectオブジェクト

open(host、int port、user、pass、[timeout])を期待する–  SSH接続用に指定されたポートを開きます

  • @param文字列 host –接続するホスト名
  • @パラメータ intポート– 開く指定ポート
  • @パラメータ文字列 ユーザー– ユーザー名の資格情報
  • @param文字列 パス –パスワード資格情報
  • @param 整数 タイムアウト –秒単位の接続タイムアウト

open(host、int port、user、pass)を期待する– SSH接続用に指定されたポートを開きます

  • @param文字列 host –接続するホスト名
  • @パラメータ intポート– 開く指定ポート
  • @パラメータ文字列 ユーザー– ユーザー名の資格情報
  • @param文字列 パス –パスワード資格情報

オブジェクトメソッド

send(コマンド) –Expect接続に文字列を送信します

  • @param文字列 command –実行するコマンドのフルパス
  • @return ボイド

expect(match_regex) –提供された正規表現で一致するものが見つかるまで、接続からテキストを読み取ります

  • @param string | array 正規表現の一致 –リモートテキストが一致する正規表現文字列または正規表現文字列の配列
  • @return ボイド

前() –現在一致しているexpect()と以前に一致したexpect()の間の出力を取得します

  • @return文字列 before_match –最新の正規表現を期待する前のテキスト

マッチした() –現在一致している文字列を取得します

  • @return文字列 現在の一致 –最新の期待正規表現と一致するテキスト

期待閉じる() –外部プロセスが完了するまで待ってから、閉じます

  • @return ボイド

(Linuxで言うところのstdout)() –外部プロセスの標準出力を取得します

  • @return文字列 (Linuxで言うところのstdout) –プロセス標準出力

stderr() –外部プロセスの標準エラーを取得します

  • @return文字列 stderr –プロセス標準出力

終了値() –外部プロセスから終了コードを取得します

  • @return int 終了値 –プロセス終了コード

使用上の注意を期待する

正規表現の選択

前述のように、cli.expect(正規表現)ヒットするまですべてのコンテンツを読み取るようにプロセスに指示します 正規表現 次に、cli.before()はその正規表現の前のすべての出力を返します。 CLIプロンプトをcli.expect(regex)パラメーターとして使用するのが一般的ですが、生成された出力に同じプロンプト文字も含まれている場合、出力キャプチャはその時点で停止します。 このため、期待する正規表現を選択する際には注意が必要です。 可能であれば、期待される文字列を一意にする必要があります。

たとえば、snmpd.confファイルの内容を「cat」したい場合(行をコメントアウトするための「#」記号が含まれている場合があります)、単純な「#」を使用してプロンプトに戻ることを示すことはできません。 代わりに、ホストの完全なsshプロンプトをキャプチャできます。

// initialize a variable to contain the actual host prompt
def actualPrompt = "";

// initiate an ssh connection to the host and wait for the "# " prompt
cli = Expect.open("192.168.211.129", "root", "xxxxxxx");
cli.expect("# ");

// capture the text preceding the initial occurrence of the  "# " string, and use that as the actual prompt
// e.g. if the prompt is "bash-3.2 # ", we'll set actualPrompt to "bash-3.2"
cli.before().eachLine
{ line ->
    actualPrompt = line;
}

// concatenate the snmpd.conf file,  and use the actual prompt as your expect regex
cli.send("cat /etc/snmp/snmpd.conf\n");

// now we expect the actual prompt character, and won't get tripped up by comment characters in the config file
cli.expect(actualPrompt + "# ");

プロンプトをエスケープする

sshホストのプロンプト文字が$の場合、この文字はエスケープして、スクリプト内で一重引用符で囲む必要があります。

cli.expect( '\);
記事上で