【想定している機能&環境】

ZabbixやNagiosなどの監視システムは様々な機器を監視し、アラートを飛ばしてくれる便利なシステムですが、自身がダウンした時はアラートを飛ばせません。そういうとこ好きです。

今回は以下の内容と環境を想定してVBSでスクリプトを作成しました。

◆実装機能

 ・監視は別端末で行う。今回はWindows10端末(操作端末)
 ・オペレータが監視システムの機能停止をすぐに気付くようにすること
 ・アラートを停止した時間が自動でログ取得されること
 ・どのような障害内容なのかおおよそ判別がつくようにログ取得すること

◆検証環境

 ●Zabbixサーバ
 ・CentOS 7.4
 ・Zabbix 3.4.3(3.0.9でもたぶん大丈夫です)
 ・MySQL 5.7.20
 ・MySQLのポート開放をFireWallで実施済み
 ・MySQLへのリモートアクセス権限をZabbixユーザにのみ許可済み

 ●操作端末
 ・Windows10
 ・ODBCでMySQLへリモート接続可能
 ・24時間365日管理者権限でログインしている

【準備したこと】

・MySQLへのリモート接続

1.FireWallへのポート許可設定
ODBC接続時にMySQLサーバのIPアドレスとポートを指定して
リモート接続します。よって事前にFireWall設定で3306ポートの許可をする必要があります。

キャプチャ.PNG

2.MySQLのリモートアクセス権限をZabbixユーザにのみ許可
許可しないとODBC接続時にはじかれてエラーが出ます。

<参考URL>
MySQLに外部ホストから接続できるように設定する

大変参考になりました。ありがとうございます。

3.MySQL ODBC DriverをWindows端末へインストールする
以下のサイトから「mysql-connector-odbc-5.3.10-winxx.msi」をダウンロードし、
MySQL ODBC Driverを利用できるようにしておいてください。

Connector/ODBC 5.3.10

インストール後はODBCデータソースアドミニストレータのドライバータブから、
選択肢として表示されるようになっています。

キャプチャ.PNG

ちなみにMariaDBであればMariaDB ODBC Driverをインストールすれば利用できます。

【ツール群の説明】

本ツールに使用するファイルの説明をします。

◆zabbix_check_zabbixs01.vbs
本ツールです。スクリプトの詳細な記載は後述しますが、
ここではアラートが通知されるタイミングのみ記載します。

 ・[CHECK01エラー]:ODBC接続失敗時
 ・[CHECK02エラー]:DBでのクエリ発行失敗時
 ・[CHECK03エラー]:DB更新が一定時間未更新時
 ・[CHECK04エラー]:WebGUIへの接続不可時

◆zabbix_check_zabbixs01.log
接続結果を出力するログです。
成功時と上記アラートの内容を出力します。
以下がサンプルです。

zabbix_check_zabbixs01.log
2018/03/24-10:35:47 [CHECK_01] [Microsoft][ODBC Driver Manager] データ ソース名および指定された既定のドライバーが見つかりません。
2018/03/24-12:26:57 [CHECK_01] [MySQL][ODBC 5.3(w) Driver]Can't connect to MySQL server on 'xxx.xxx.xxx.xxx' (10060)
2018/03/24-12:45:51 [CHECK_03] [MySQL] 15分以内に更新されたレコードが見つかりません。
2018/03/24-17:25:01 [CHECK_04] [WEB] Zabbix Web Serverの応答異常です。12029
2018/03/24-17:15:00 [ZabbixCheck] AllSuccess

CHECK02はなかなか出せないので諦めました。

◆xxxxx.wav
任意のwavファイルをアラート音として使います。

◆playloop.exe
上記wavファイルをループさせるために使います。
コマンドプロンプト上で、

playloop.exe xxxxx.wav

と入力して実行すると、wavファイルを実行し続けます。
以下はPowerShellで回してる画像です。
キャプチャ.PNG

以下のサイトからダウンロード可能です。
コマンドライン録音ソフト/再生ソフト

◆alarm_on.log
なんらかの要因でアラートが発報した時に、
画面が出力され、任意のIDを入力してもらうように促されます。
その時に入力した時刻とPlayloop.exeのプロセス数が明記されるログファイルです。
以下は画面とログファイルの内容です。
キャプチャ.PNG

Playloop.exeのプロセス監視をしている理由は、
同exeがループする度にプロセス数が無限増加するとの報告があるためです。
スクリプト内で起動数に制限をかけていますが、念のため記録しています。

【zabbix_check_zabbixs01.vbs】

以下が構文です。

zabbix_check_zabbixs01.vbs
Option Explicit

'************************
'初期設定変数
'************************
Dim systemname

systemname = "監視システム(ZABBIX)"

'************************************
'変数定義
'************************************

Dim wavfile

Dim strComputer
Dim objWMIService
Dim colItems
Dim objWshShell

Dim objProc
Dim istring

Dim strFileName
Dim strFilePath
Dim strScriptPath
Dim objFileSys
Dim objOutFile

Dim cn
Dim rs
Dim fs

Dim strdriver
Dim strserver
Dim strdb
Dim struser
Dim strpass
Dim ConnectionString

Dim fifteen_minutes_ago
Dim Query
Dim rs_count
Dim Check01
Dim Check02
Dim Check03
Dim check_name
Dim check_no

Dim count_num

Dim oHttp

Const ForAppending = 8 '書き込み(追記モード)

'****************************************
'オブジェクト作成
'****************************************

'=== wavfile指定
wavfile = "C:\zabbix\Tools\xxx.wav"



'=== ログファイル名作成
strFileName = "zabbix_check_zabbixs01.log"
strScriptPath = Replace(WScript.ScriptFullName,WScript.ScriptName,"")
Set objFileSys = WScript.CreateObject("Scripting.FileSystemObject")
strFilePath = objFileSys.BuildPath(strScriptPath,strFileName)

'=== ログファイル存在チェック(なければ作成)
If (objFileSys.FileExists(strFilePath)) = 0 Then
    Set objOutFile = objFileSys.CreateTextFile(strFilePath)
    objOutFile.close
End If

Set Cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
Set fs = CreateObject("Scripting.FileSystemObject")

'=== ODBC接続の準備
strdriver = "{MySQL ODBC 5.3 Unicode Driver}"
strserver = "xxx.xxx.xxx.xxx"
strdb = "zabbix"
struser = "zabbix"
strpass = "xxxxxxxxx" 'DB接続するユーザのPASSWORDを入力

ConnectionString = _
    "Provider=MSDASQL" &_
    ";Driver=" & strdriver &_
    ";Server=" & strserver &_
    ";DATABASE=" & strdb &_
    ";UID=" & struser &_
    ";PWD=" & strpass &_
    ";"


'***********************************************
'ODBC接続
'***********************************************
On Error Resume Next
Cn.Open ConnectionString

If Err.Number = 0 Then
    'judgment_success "[CHECK_01]"
Else
    judgment_error "[CHECK_01]"
    Wscript.Quit
End If


On Error GoTo 0


'**********************************************
'レコードセット取得
'**********************************************


'=== 現時刻から15分前のUNIX Timeを算出
fifteen_minutes_ago = DateDiff("s", "1970/01/01 00:00:00", DateAdd("n", -15, Now())) -32400

'=== 15分前から現時刻までに「history_uint」テーブルへ書き込まれたレコード数をカウント
Query = "select count(itemid) from `history_uint` where clock > " & fifteen_minutes_ago

On Error Resume Next
rs.Open Query, Cn


If Err.Number = 0 Then
    'judgment_success "[CHECK_02]"
Else
    judgment_error "[CHECK_02]"
    Cn.Close
    Wscript.Quit
End If

On Error GoTo 0


'********************************************
'レコードチェック
'********************************************

    rs.MoveFirst
Do While Not rs.EOF
    rs_count = rs.Fields(0).Value



    If rs_count <> "0" Then
        'judgment_success "[CHECK_03]"
    Else
        Err.Description = "[MySQL] 15分以内に更新されたレコードが見つかりません。"
        judgment_error "[CHECK_03]"
        rs.Close
        Cn.Close
        Wscript.Quit
    End If

    rs.Movenext
Loop

'********************************************
'レコードセットクローズ
'********************************************
rs.Close

'********************************************
'接続解除
'********************************************
Cn.Close

'********************************************
'WEB応答監視
'********************************************

Set oHttp = CreateObject("MSXML2.XMLHTTP")

On Error Resume Next
oHttp.Open "GET", "http://xxx.xxx.xxx.xxx/zabbix", False
oHttp.Send

If (oHttp.Status >= 200 And oHttp.Status < 300) Then
    'judgment_success "[CHECK_04]" & oHttp Status
Else
    Err.Description = "[WEB] Zabbix Web Serverの応答異常です。" & oHttp.Status
    judgment_error "[CHECK_04]"
    Wscript.Quit
End If

    judgment_success " [ZabbixCheck] All"

On Error Goto 0

'****************************************
'オブジェクト解放
'****************************************
Set wavfile = Nothing

Set strComputer = Nothing
Set objWMIService = Nothing
Set colItems = Nothing
Set objWshShell = Nothing

Set strFileName = Nothing
Set strFilePath = Nothing
Set strScriptPath = Nothing
Set objFileSys = Nothing
Set objOutFile = Nothing

Set cn = Nothing
Set rs = Nothing
Set fs = Nothing

Set strdriver = Nothing
Set strserver = Nothing
Set strdb = Nothing
Set struser = Nothing
Set strpass = Nothing
Set ConnectionString = Nothing

Set fifteen_minutes_ago = Nothing
Set Query = Nothing
Set rs_count = Nothing
Set Check01 = Nothing
Set Check02 = Nothing
Set Check03 = Nothing
Set check_name = Nothing
Set check_no = Nothing

Set count_num = Nothing

Set oHttp = Nothing


'*****************************************
'チェック正常時のログ出力
'*****************************************
Sub judgment_success(check_no)
    '====== ログに記録 : 日付-時間
    Set objOutFile = objFileSys.OpenTextFile(strFilePath,ForAppending)
    objOutFile.WriteLine    date & "-" & time & check_no & "Success"
    objOutFile.Close
End Sub


'*****************************************
'チェック異常時のログ出力&再生
'*****************************************
Sub judgment_error(check_no)
'====== ログに記録 : 日付-時間
Set objOutFile = objFileSys.OpenTextFile(strFilePath,ForAppending)
objOutFile.WriteLine    date & "-" & time & " " & check_no & " " & Err.Description
objOutFile.Close

'====== オブジェクト宣言
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objWshShell = WScript.CreateObject("WScript.Shell")
Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'playloop.exe'")


'====== ファイル名生成
strFileName = "alarm_on.log"
strScriptPath = Replace(WScript.ScriptFullName,WScript.ScriptName,"")
Set objFileSys = WScript.CreateObject("Scripting.FileSystemObject")
strFilePath = objFileSys.BuildPath(strScriptPath,strFileName)

'====== ファイル存在チェック(なければ作成)
If (objFileSys.FileExists(strFilePath)) = 0 Then
    Set objOutFile = objFileSys.CreateTextFile(strFilePath)
    objOutFile.Close
End If

'====== プロセス生成

'===Playloop.exeが際限なく起動することを防ぐためif文で防ぐ

If colItems.count <= 1 then

'=== アラーム音を再生
 objWshShell.Run """C:\zabbix\Tools\playloop.exe""" & wavfile,0,false

'=== ログに記録 : 日付-時間  プロセス起動個数
Set objOutFile = objFileSys.OpenTextFile(strFilePath,ForAppending)
    objOutFile.WriteLine   date & "-" & time & "   " & colItems.count
    objOutFile.Close
Else
'=== ログに記録
Set objOutFile = objFileSys.OpenTextFile(strFilePath,ForAppending)
objOutFile.WriteLine   date & "-" & time & "   " & colItems.count & "(The maximum of processes has reached. Stop this process.)"
objOutFile.Close

Wscript.Quit
End If

'====== プロセス停止処理

'=== 再度colItemsを取得する
Set objWshShell = WScript.CreateObject("WScript.Shell")
Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'playloop.exe'")

'=== IDを入力
Do
    istring = InputBox(vbCrLf & "◆" & systemname & vbCrLf & vbCrLf & "監視システムが停止しました。" & vbCrLf & vbCrLf & vbCrLf & vbCrLf & "##### 自分のIDを入力してください。#####" & vbCrLf & vbCrLf & "例) 012345 <6桁>","アラーム音停止",,100,100)
If IsEmpty(istring) Then
    WScript.Quit 1
End If
    Loop While Len(istring) <> 6


If colItems.count <> 0 Then
    For Each objProc In colItems
    objProc.Terminate
    Next

End If

'*****************************************
'オブジェクト解放
'*****************************************
Set istring = Nothing
Set wavfile = Nothing

Set strComputer = Nothing
Set objWMIService = Nothing
Set colItems = Nothing
Set objWshShell = Nothing

Set strFileName = Nothing
Set strFilePath = Nothing
Set strScriptPath = Nothing
Set objFileSys = Nothing
Set objOutFile = Nothing

End Sub

これならいつ監視サーバが落ちても、
オペレータはすぐに気づけますし、深夜連絡もらってもログを見てもらえれば
障害内容がおおよそ把握できるので初動も早く対応できると思います。

タスクスケジューラに任意の時間で繰り返すように設定すれば完了です。

もし活用される際はファイルパスやファイル名、パスワードなどなどを
書き換えてください。

【作ってる時に思ったこと(余談)】

・DB更新頻度確認フェーズ(CHECK02)で、
 更新確認しに行っているテーブルはhistory_uintを選んでいます。
 history系のテーブルであれば15分以内であれば更新されていると思いますが、
 今回は一番更新頻度の高そうなhistory_uintを選んでいます。
 気になる方は確認テーブルを適宜変更してください。

・playloop.exeをループ処理に採用したのは、当初使用していたwmplayer.exeの処理が
 重く、困っていたためです。

・アラート発生画面の入力値は6文字打たないと通らないイライラ設計にしています。
 苦しんでください。あとここで担当者固有のIDをオペレータに打たせてログに記録したかったのですが、
 そこまでの詳細な設計には辿り着けませんでした。いらない機能です。苦しんでください。

・以下のサイトで女性音声のテキスト読み上げwavファイルを作れます。

 CMAN WEB便利ノート

 でもどうしても女性の声がきもかったのでやめました。

【これからの課題】

上記の余談でも書いているような細かい点を改善できればなと思います。

また今後、今までVBSで書き溜めたスクリプトをPowerShellに翻訳することにしましたので、
翻訳完了したらアップします。

VBSは変数やオブジェクト管理が面倒でまだ慣れないです。
シェルスクリプトは組んでて楽しいです。

以上です。