←前
目次
次→

Luaスクリプト機能

$Date: 2009/10/13 02:58:59 $

もう少しLuaスクリプト

メール送信とテーブル

今度は、pingの結果をメールで送信してみましょう。メール送信には、rt.mail関数を利用します。

[ping5.lua] ダウンロード

function ping(dst)
  flag, outputs = rt.command("ping -c 1 " .. dst)
  if flag then
    first_line = string.match(outputs, "^(.-)\n")
    return first_line
  else
    return "ERROR: ping -c 1 " .. dst
  end
end

text = ""
for i = 1, #arg do
  text = text .. ping(arg[i]) .. "\n"
end

mail_table = {}
mail_table.smtp_address = "mx.example.jp"
mail_table.from = "from@example.jp"
mail_table.to = "to@exapmle.jp"
mail_table.subject = "PING結果"
mail_table.text = text

ret = rt.mail(mail_table)

if (not ret) then
  print("メール送信失敗")
end
    

まず、ping関数で各IPアドレスにpingした結果の文字列を、変数textに集めます。

text = ""
for i = 1, #arg do
  text = text .. ping(arg[i]) .. "\n"
end
    

そして、それをrt.mailでメール送信します。rt.mailの引数はテーブルで渡します。

mail_table = {}
mail_table.smtp_address = "mx.example.jp"
mail_table.from = "from@example.jp"
mail_table.to = "to@exapmle.jp"
mail_table.subject = "PING結果"
mail_table.text = text

ret = rt.mail(mail_table)
    

まず最初の行で、mail_tableという変数に空のテーブルを代入しています。これが無いと、その後の各要素の代入がエラーになります。

その後、smtp_addressfromなどの要素を代入し、最後にrt.mailの実引数としてmail_tableを渡しています。

smtp_addressはメール送信に使うSMTPサーバーのIPアドレスまたはホスト名を、fromtoにはそれぞれメールの送信元と送信先のメールアドレスを設定します。これら3つの要素は必ず存在しなくてはいけません。

subjectはメールのサブジェクト、textはメール本文を設定します。これらは無くてもエラーにはなりませんが、普通は設定しておきます。

以上、5つの要素を持ったテーブルを、rt.mailに引数として与えれば、後はルーターがメールを送信してくれます。メールの送信結果は、rt.mailの戻り値として返されるので、それを変数retに代入しています。retはメール送信が成功したかどうかで、成功した場合はtrue、失敗の場合はfalseです。

メール送信に失敗した場合だけ、以下のようにエラーメッセージを表示しています。

if (not ret) then
  print("メール送信失敗")
end
    

ここで、変数retの前にnotというキーワードが置かれていますが、これはnot演算子というもので、retの値を反転することを意味します。したがって、このif文の判定はretfalseのときに真となります。

さて、突然、テーブルというものが出てきましたが、これは、複数のデータを一まとめにしたものと考えてください。今回の場合だと、SMTPサーバー、メールアドレス、メール本文など、多数のデータがメール送信には必要なので、それらを一まとめにしたテーブルをrt.mailの引数にしている、ということです。

テーブルは、複数のデータが一まとめになったものです。それら複数のデータを要素と呼びます。要素は、キーと値という二つのデータの組になっています。キーはテーブルの中の場所を特定するために用いられ、値は要素を評価したときの値になります。

        テーブル

        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
        |    キー    |    値      |    ← 要素
        +-------------------------+
    

テーブルの中で、要素ごとにキーはすべて異なります。キーには、nil以外のLua言語で利用できるすべてのデータが利用できますが、通常は数値か文字列をキーにすることが多いと思います。

テーブルの各要素にアクセスするときには、テーブル名の後に角カッコ[ ]をつけて、角カッコの中にキーの値を書きます。たとえば、テーブルtblの、キーが数値10の要素にアクセスするには、tbl[10]となります。

先の説明に出てきた、luaコマンドの引数を表すargもテーブルです。数値のみをキーに持つテーブルになっています。

キーが文字列の場合は、たとえばキーが"str"であれば、tbl["str"]となりますが、これは、ピリオド.を用いて、tbl.strと書くこともできます。どちらの書き方をしてもまったく同じ意味になります。

ping5.luaのコードで言えば、テーブルmail_tableの各要素を設定していますが、それらはすべてキーが文字列型になっています。これらは、以下のような書き方でもアクセスできるというわけです。

mail_table.smtp_address ⇔ mail_table["smtp_address"]
mail_table.from         ⇔ mail_table["from"]
mail_table.to           ⇔ mail_table["to"]
mail_table.subject      ⇔ mail_table["subject"]
mail_table.text         ⇔ mail_table["text"]
    

また、最初にmail_tableには空のテーブルを代入しました。空のテーブルとは、要素が一つも入っていないテーブルのことで、{ }と書きます。

mail_table = {}
    

この{ }は実際には、テーブル構築子という名前で、空のテーブルだけではなく、波カッコの間に要素を書くことで、中身のあるテーブルを作り出すこともできます。たとえば、ping5.luaのmail_tableの各要素に値を代入しているところは、以下のようにも書くことができます。

mail_table = {
  smtp_address = "mx.example.jp",
  from = "from@example.jp",
  to = "to@exapmle.jp",
  subject = "PING結果",
  text = text
}
    

波カッコの間に、「キー = 値」の組をコンマ,で区切りながら書きます。キーが文字列の場合は上のようにそのまま書いてしまえばいいのですが、キーが数値の場合は、角カッコを使います。

tbl = { [1] = "Hello", [10] = "World" }
    

また、「キー = 値」ではなく、値だけを書くと、1から始まる連番の数値がキーであるとみなされます。

tbl = { "Hello", "World" }  -- { [1] = "Hello", [2] = "World" } と同じ意味
    

このように、テーブルは複数のデータをまとめて扱うことができるので、さまざまな用途に用いることができます。rt.mail関数のように多数のパラメータをまとめるために用いたり、一連のデータをひとまとめにして処理するために用いたりすることができます。

キーがすべて1以上の数値で、かつキーが連続している、つまり、1からキーの最大値まで隙間無く要素が存在するテーブルのことを、特に配列と呼びます。配列は、他のプログラミング言語でも普通に使えるデータ構造であり、さまざまなアルゴリズムが配列を使って実装されています。そのため、Lua言語でも特に配列を他のテーブルと区別して扱うことがあります。

定期的な実行

さて、ping5.luaでは指定した相手にpingをして、その結果をメールで送信できるようになりました。これを使えば、たとえばサーバーがちゃんと動作しているかどうかを監視することができるようになります。このスクリプトを定期的、たとえば1時間ごとに動作させれば、1時間ごとにサーバーの動作状態を監視できるというわけです。では、どのようにすればいいでしょうか。

最初に考えられる方法は、Luaスクリプトの中で1時間ごとに処理を繰り返すという方法です。そのためには、一定時間処理を遅延させる、rt.sleepを用います。

[ping6.lua] ダウンロード

function ping(dst)
  flag, outputs = rt.command("ping -c 1 " .. dst)
  if flag then
    first_line = string.match(outputs, "^(.-)\n")
    return first_line
  else
    return "ERROR: ping -c 1 " .. dst
  end
end

mail_table = {}
mail_table.smtp_address = "mx.example.jp"
mail_table.from = "from@example.jp"
mail_table.to = "to@exapmle.jp"
mail_table.subject = "PING結果"

while true do
  text = ""
  for i = 1, #arg do
    text = text .. ping(arg[i]) .. "\n"
  end

  mail_table.text = text

  ret = rt.mail(mail_table)

  if (not ret) then
    rt.syslog("info", "メール送信失敗")
  end

  rt.sleep(3600)
end
    

ping6.luaでは、ping5.luaのうち、pingを実行してメールを送信する部分をwhile true doendで囲みました。条件式がtrueなので、このループは終わることなく、永遠に回り続けます。また、ループのたびに変わるのは本文だけなので、それ以外のrt.mailへのパラメータはループの外に出しました。

ループの最後では、rt.sleep(3600)で3600秒、つまり1時間、実行を遅延しています。これにより、ping、メール送信が1回行われると、1時間待って、再度、ping、メール送信を行う、という処理になります。このrt.sleepが無いと、ping、メール送信の処理が待ち時間無しで連続して行われてしまいますので、注意してください。

また、ping6.luaでは、メール送信に失敗したときの情報を、rt.syslogでログに出力するようにしました。

このように永遠に実行を続け終わらないLuaスクリプトを作ることができますが、本当に終わらないのでは、少し困ってしまいますね。そのような場合のために、実行中のLuaスクリプトの状態を表示する、show status luaコマンドと、実行中のLuaスクリプトを強制的に終了する、terminate luaコマンドが存在します。

# lua /lua/ping6.lua 192.168.100.200
# show status lua
Luaライブラリバージョン:        Lua 5.1.4
Luaスクリプト機能バージョン:    1.0

[running]
LuaタスクID (状態):  1  (RUN)
走行トリガー:        'lua' コマンド
コマンドライン:      lua /lua/ping6.lua 192.168.100.200
スクリプトファイル:  /lua/ping6.lua
開始日時:            2009/09/21 16:41:01
経過時間:            6秒
:
# terminate lua 1
    

実行中のLuaスクリプトの状態を、show status luaコマンドで表示し、終了したいタスクのタスクIDをterminate luaコマンドで指定します。

schedule atコマンドで定期的に実行

終わらないLuaスクリプトもいいですが、schedule atコマンドでLuaスクリプトを定期的に実行する方法もあります。1回だけpingを実行するping5.luaを、schedule atコマンドで1時間ごとに実行してみましょう。

# schedule at 1 *:00 * lua /lua/ping5.lua 192.168.100.200
    

この方法の場合、万が一、ping5.luaがどこかで無限ループになって終わらないスクリプトになってしまった場合に、複数のスクリプトが同時に走るようなことになってしまいます。そのため、schedule atコマンドで登録する前に十分なテストが必要になります。

このように、定期的に処理を行うには2つの方法があります。どちらがいいとは単純には言えませんが、処理を正式に運用するときにはschedule atコマンドを用い、一時的な処理や、まだテスト段階というときにはrt.sleepを用いる、というのは一つの考え方だと思います。


←前 目次
次→