【想定している機能&環境】
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ポートの許可をする必要があります。
2.MySQLのリモートアクセス権限をZabbixユーザにのみ許可
許可しないとODBC接続時にはじかれてエラーが出ます。
<参考URL>
MySQLに外部ホストから接続できるように設定する
大変参考になりました。ありがとうございます。
3.MySQL ODBC DriverをWindows端末へインストールする
以下のサイトから「mysql-connector-odbc-5.3.10-winxx.msi」をダウンロードし、
MySQL ODBC Driverを利用できるようにしておいてください。
インストール後はODBCデータソースアドミニストレータのドライバータブから、
選択肢として表示されるようになっています。
ちなみにMariaDBであればMariaDB ODBC Driverをインストールすれば利用できます。
【ツール群の説明】
本ツールに使用するファイルの説明をします。
◆zabbix_check_zabbixs01.vbs
本ツールです。スクリプトの詳細な記載は後述しますが、
ここではアラートが通知されるタイミングのみ記載します。
・[CHECK01エラー]:ODBC接続失敗時
・[CHECK02エラー]:DBでのクエリ発行失敗時
・[CHECK03エラー]:DB更新が一定時間未更新時
・[CHECK04エラー]:WebGUIへの接続不可時
◆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で回してる画像です。
以下のサイトからダウンロード可能です。
コマンドライン録音ソフト/再生ソフト
◆alarm_on.log
なんらかの要因でアラートが発報した時に、
画面が出力され、任意のIDを入力してもらうように促されます。
その時に入力した時刻とPlayloop.exeのプロセス数が明記されるログファイルです。
以下は画面とログファイルの内容です。
Playloop.exeのプロセス監視をしている理由は、
同exeがループする度にプロセス数が無限増加するとの報告があるためです。
スクリプト内で起動数に制限をかけていますが、念のため記録しています。
【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ファイルを作れます。
でもどうしても女性の声がきもかったのでやめました。
【これからの課題】
上記の余談でも書いているような細かい点を改善できればなと思います。
また今後、今までVBSで書き溜めたスクリプトをPowerShellに翻訳することにしましたので、
翻訳完了したらアップします。
VBSは変数やオブジェクト管理が面倒でまだ慣れないです。
シェルスクリプトは組んでて楽しいです。
以上です。