ちょっとした端末監視が必要になった時に利用したLUAを公開します。
別途 SLACKのWebhook設定が必要です。
json.lua を取り込んでいます。
--------------------------## 設定値 ##--------------------------------
-- 実行している拠点名
branch_name = "テスト拠点"
-- 監視間隔(1 - 864000 秒)
idle_time = 30
-- ping を実行する宛先 IP アドレス
dst_tbl = {
"192.168.100.201",
"192.168.100.202"
}
-- ping への応答がない、または応答が回復したと判断する連続回数(1, 2 ..)
count = 6
-- 応答が回復したときにも通知を送るかどうか(送る: true / 送らない: false)
down_mail = true
-- Slack の設定
slack_tbl = {
webhook_url = "https://hooks.slack.com/services/xxxxxxxxx/yyyyyyyyy/zzzzzzzzzzzzzzzzzzzzzzzz",
channel = "#general",
username = "YAMAHA LUA Notice",
icon_emoji = ":warning:"
}
-- 通知の送信に失敗したときに出力する SYSLOG のレベル(info, debug, notice)
log_level = "notice"
----------------------## 設定値ここまで ##----------------------------
json = require("json")
------------------------------------------------------------
-- ping を実行し、到達したかどうかを返す関数 --
------------------------------------------------------------
function ping_reach(adr)
local rtn, str, loss
local reach = false
local cmd = "ping " .. adr
local ptn = "(%d+)%.%d+%%"
rtn, str = rt.command(cmd)
if (rtn) and (str) then
loss = str:match(ptn)
if (loss) then
loss = tonumber(loss)
if (loss == 0) then
reach = true
end
end
end
return rtn, reach, str
end
--------------------------------------------------------------
-- 連続何回 ping に応答がないかを示すカウンターの処理関数 --
--------------------------------------------------------------
function count_proc(t, reach, th)
local rtn = 0
if (not reach) then
if (not t.flag) then
t.ng = t.ng + 1
if (t.ng == th) then
rtn = 1
t.flag = true
end
else
if (t.ok > 0) then
t.ok = 0
end
end
else
if (t.flag) then
t.ok = t.ok + 1
if (t.ok == th) then
rtn = -1
t.flag = false
t.ng = 0
t.ok = 0
end
else
if (t.ng > 0) then
t.ng = 0
end
end
end
return rtn
end
------------------------------------------------------------
-- Slack通知メッセージを作成する関数 --
-- 戻り値: メッセージ文字列(状況変化がない場合は空文字) --
------------------------------------------------------------
function make_pingmsg(tbl, reach, adr, cnt, sec, down)
local rtn
local str = ""
local emoji = ""
rtn = count_proc(tbl, reach, cnt)
-- rtn = 1: 異常発生、rtn = -1: 復旧、rtn = 0: 状況変化なし
if (rtn < 0) then
-- 復旧時
if (down) then
emoji = ":white_check_mark:"
str = emoji .. " *Ping応答が回復しました*\n"
str = str .. "```送信先: " .. adr .. "\n"
str = str .. "監視間隔: " .. sec .. "秒```"
end
elseif (rtn > 0) then
-- 異常発生時
emoji = ":x:"
str = emoji .. " *Ping応答がありません*\n"
str = str .. "```送信先: " .. adr .. "\n"
str = str .. "応答がなかった回数: " .. cnt .. "回 " .. "\n"
str = str .. "監視間隔: " .. sec .. "秒```"
end
-- rtn = 0 の場合は str = "" のまま(状況変化なし)
return str
end
------------------------------------------------------------
-- 連続不応答回数を記録するテーブルの初期化関数 --
------------------------------------------------------------
function init_count_tbl(n, cnt_t)
for i = 1, n do
cnt_t[i] = {ng = 0, ok = 0, flag = false}
end
end
------------------------------------------------------------
-- 現在の日時を取得する関数 --
------------------------------------------------------------
function time_stamp()
local t
t = os.date("*t")
return string.format("%d/%02d/%02d %02d:%02d:%02d",
t.year, t.month, t.day, t.hour, t.min, t.sec)
end
------------------------------------------------------------
-- Slackに通知を送信する関数 --
------------------------------------------------------------
function send_slack(message)
local req_t = {}
req_t.url = slack_tbl.webhook_url
req_t.content_type = "application/json;charset=UTF-8"
req_t.method = "POST"
local req_b = {
channel = slack_tbl.channel,
icon_emoji = slack_tbl.icon_emoji,
username = slack_tbl.username,
text = message
}
req_t.post_text = json.encode(req_b)
local res_t = rt.httprequest(req_t)
return res_t
end
------------------------------------------------------------
-- メインルーチン --
------------------------------------------------------------
local rtn, reach, str, adr
local cnt_tbl = {}
init_count_tbl(#dst_tbl, cnt_tbl)
while (true) do
local notification_text = ""
for i, adr in ipairs(dst_tbl) do
rtn, reach, str = ping_reach(adr)
if (rtn) then
local msg = make_pingmsg(cnt_tbl[i], reach, adr, count, idle_time, down_mail)
if msg:len() > 0 then
notification_text = notification_text .. msg .. "\n\n"
end
else
notification_text = notification_text ..
":warning: *Pingコマンドエラー*\n```送信先: " .. adr .. "\n" ..
"エラー: " .. str .. "```\n\n"
end
end
if (notification_text:len() > 0) then
local full_message = string.format("*[%s - Ping監視通知] %s*\n\n%s",
branch_name, time_stamp(), notification_text)
local res = send_slack(full_message)
if (not res) then
rt.syslog(log_level, "failed to send Slack notification. (pingCheck_slack.lua)")
end
end
rt.sleep(idle_time)
end
過去に同じようなことを書いたときのもの。