Zabbix之通过Graphid下载图片——第二版
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zabbix之通过Graphid下载图片——第二版相关的知识,希望对你有一定的参考价值。
阅读本篇之前要先读上文:https://blog.51cto.com/181647568/2480079根据上一次的Powershell版本的图片下载脚本之后,我收到了一些反馈。
1.上一个脚本通过dll实现访问数据库,通过sql语句来获取graphid,这就意味着除了脚本之外还要有一个dll。而且数据库必须开放访问权限,这就意味着安全性降低。而且并不是所有的生产环境都可以有机会进数据,或者修改数据库的。
2.cookie需要手动在浏览器里抓包获取。这对一些IT来说有难度。
3.itemkey是写死的,如果要多itemkey的图片需要运行多次脚本,或者修改脚本代码。
根据我一开始看的博客中的内容获得启发,我打算做一下改进。
参考文档:https://www.cnblogs.com/dreamer-fish/p/5485556.html
由于生产环境的windows上没有python,我还不能装。但是Linux上的python有不能用那个博文中的代码,缺少一些模块。所以这次我打算用vbs去实现代码。powershell的cookie,session之类的对象太复杂了。
我没有在zabbix查看graph的时候通过http抓包看到graphid产生的过程。我发现zabbix有一套独立的webapi,通过jsonrpc格式数据进行通信,但是在网页正常访问的时候是不用这套api的。
官方文档:https://www.zabbix.com/documentation/3.4/zh/manual/api
api的查询方法和sql是一样的。通过zabbix_agent_host查到hostid,根据hostid和itemkey查到itemid,再通过Itemid查到graphid。然后下载图片。这个jsonrpc的过程不需要用到cookie。而下图片的过程需要cookie。
唯一的不同是jsonrpc webapi需要取到一个authcode的过程,需要用到用户名和密码来获取,接下来的过程都需要用到这个authcode。
为了获取cookie,可能需要模拟一次登录的过程来取到cookie。登录界面index.php中通过post用户名和密码可以获取到cookie。这样我们在代码中只需要填入用户名和密码,而不是cookie,更加的友善了。
以下是代码:
On Error Resume Next
Class VbsJson
‘Author: Demon
‘Date: 2012/5/3
‘Website: http://demon.tw
Private Whitespace, NumberRegex, StringChunk
Private b, f, r, n, t
Private Sub Class_Initialize
Whitespace = " " & vbTab & vbCr & vbLf
b = ChrW(8)
f = vbFormFeed
r = vbCr
n = vbLf
t = vbTab
Set NumberRegex = New RegExp
NumberRegex.Pattern = "(-?(?:0|[1-9]d*))(.d+)?([eE][-+]?d+)?"
NumberRegex.Global = False
NumberRegex.MultiLine = True
NumberRegex.IgnoreCase = True
Set StringChunk = New RegExp
StringChunk.Pattern = "([sS]*?)([""\x00-x1f])"
StringChunk.Global = False
StringChunk.MultiLine = True
StringChunk.IgnoreCase = True
End Sub
‘Return a JSON string representation of a VBScript data structure
‘Supports the following objects and types
‘+-------------------+---------------+
‘| VBScript | JSON |
‘+===================+===============+
‘| Dictionary | object |
‘+-------------------+---------------+
‘| Array | array |
‘+-------------------+---------------+
‘| String | string |
‘+-------------------+---------------+
‘| Number | number |
‘+-------------------+---------------+
‘| True | true |
‘+-------------------+---------------+
‘| False | false |
‘+-------------------+---------------+
‘| Null | null |
‘+-------------------+---------------+
Public Function Encode(ByRef obj)
Dim buf, i, c, g
Set buf = CreateObject("Scripting.Dictionary")
Select Case VarType(obj)
Case vbNull
buf.Add buf.Count, "null"
Case vbBoolean
If obj Then
buf.Add buf.Count, "true"
Else
buf.Add buf.Count, "false"
End If
Case vbInteger, vbLong, vbSingle, vbDouble
buf.Add buf.Count, obj
Case vbString
buf.Add buf.Count, """"
For i = 1 To Len(obj)
c = Mid(obj, i, 1)
Select Case c
Case """" buf.Add buf.Count, """"
Case "" buf.Add buf.Count, "\"
Case "/" buf.Add buf.Count, "/"
Case b buf.Add buf.Count, ""
Case f buf.Add buf.Count, "f"
Case r buf.Add buf.Count, "
"
Case n buf.Add buf.Count, "
"
Case t buf.Add buf.Count, " "
Case Else
If AscW(c) >= 0 And AscW(c) <= 31 Then
c = Right("0" & Hex(AscW(c)), 2)
buf.Add buf.Count, "u00" & c
Else
buf.Add buf.Count, c
End If
End Select
Next
buf.Add buf.Count, """"
Case vbArray + vbVariant
g = True
buf.Add buf.Count, "["
For Each i In obj
If g Then g = False Else buf.Add buf.Count, ","
buf.Add buf.Count, Encode(i)
Next
buf.Add buf.Count, "]"
Case vbObject
If TypeName(obj) = "Dictionary" Then
g = True
buf.Add buf.Count, "{"
For Each i In obj
If g Then g = False Else buf.Add buf.Count, ","
buf.Add buf.Count, """" & i & """" & ":" & Encode(obj(i))
Next
buf.Add buf.Count, "}"
Else
Err.Raise 8732,,"None dictionary object"
End If
Case Else
buf.Add buf.Count, """" & CStr(obj) & """"
End Select
Encode = Join(buf.Items, "")
End Function
‘Return the VBScript representation of ``str(``
‘Performs the following translations in decoding
‘+---------------+-------------------+
‘| JSON | VBScript |
‘+===============+===================+
‘| object | Dictionary |
‘+---------------+-------------------+
‘| array | Array |
‘+---------------+-------------------+
‘| string | String |
‘+---------------+-------------------+
‘| number | Double |
‘+---------------+-------------------+
‘| true | True |
‘+---------------+-------------------+
‘| false | False |
‘+---------------+-------------------+
‘| null | Null |
‘+---------------+-------------------+
Public Function Decode(ByRef str)
Dim idx
idx = SkipWhitespace(str, 1)
If Mid(str, idx, 1) = "{" Then
Set Decode = ScanOnce(str, 1)
Else
Decode = ScanOnce(str, 1)
End If
End Function
Private Function ScanOnce(ByRef str, ByRef idx)
Dim c, ms
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "{" Then
idx = idx + 1
Set ScanOnce = ParseObject(str, idx)
Exit Function
ElseIf c = "[" Then
idx = idx + 1
ScanOnce = ParseArray(str, idx)
Exit Function
ElseIf c = """" Then
idx = idx + 1
ScanOnce = ParseString(str, idx)
Exit Function
ElseIf c = "n" And StrComp("null", Mid(str, idx, 4)) = 0 Then
idx = idx + 4
ScanOnce = Null
Exit Function
ElseIf c = "t" And StrComp("true", Mid(str, idx, 4)) = 0 Then
idx = idx + 4
ScanOnce = True
Exit Function
ElseIf c = "f" And StrComp("false", Mid(str, idx, 5)) = 0 Then
idx = idx + 5
ScanOnce = False
Exit Function
End If
Set ms = NumberRegex.Execute(Mid(str, idx))
If ms.Count = 1 Then
idx = idx + ms(0).Length
ScanOnce = CDbl(ms(0))
Exit Function
End If
Err.Raise 8732,,"No JSON object could be ScanOnced"
End Function
Private Function ParseObject(ByRef str, ByRef idx)
Dim c, key, value
Set ParseObject = CreateObject("Scripting.Dictionary")
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "}" Then
Exit Function
ElseIf c <> """" Then
Err.Raise 8732,,"Expecting property name"
End If
idx = idx + 1
Do
key = ParseString(str, idx)
idx = SkipWhitespace(str, idx)
If Mid(str, idx, 1) <> ":" Then
Err.Raise 8732,,"Expecting : delimiter"
End If
idx = SkipWhitespace(str, idx + 1)
If Mid(str, idx, 1) = "{" Then
Set value = ScanOnce(str, idx)
Else
value = ScanOnce(str, idx)
End If
ParseObject.Add key, value
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "}" Then
Exit Do
ElseIf c <> "," Then
Err.Raise 8732,,"Expecting , delimiter"
End If
idx = SkipWhitespace(str, idx + 1)
c = Mid(str, idx, 1)
If c <> """" Then
Err.Raise 8732,,"Expecting property name"
End If
idx = idx + 1
Loop
idx = idx + 1
End Function
Private Function ParseArray(ByRef str, ByRef idx)
Dim c, values, value
Set values = CreateObject("Scripting.Dictionary")
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "]" Then
ParseArray = values.Items
Exit Function
End If
Do
idx = SkipWhitespace(str, idx)
If Mid(str, idx, 1) = "{" Then
Set value = ScanOnce(str, idx)
Else
value = ScanOnce(str, idx)
End If
values.Add values.Count, value
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "]" Then
Exit Do
ElseIf c <> "," Then
Err.Raise 8732,,"Expecting , delimiter"
End If
idx = idx + 1
Loop
idx = idx + 1
ParseArray = values.Items
End Function
Private Function ParseString(ByRef str, ByRef idx)
Dim chunks, content, terminator, ms, esc, char
Set chunks = CreateObject("Scripting.Dictionary")
Do
Set ms = StringChunk.Execute(Mid(str, idx))
If ms.Count = 0 Then
Err.Raise 8732,,"Unterminated string starting"
End If
content = ms(0).Submatches(0)
terminator = ms(0).Submatches(1)
If Len(content) > 0 Then
chunks.Add chunks.Count, content
End If
idx = idx + ms(0).Length
If terminator = """" Then
Exit Do
ElseIf terminator <> "" Then
Err.Raise 8732,,"Invalid control character"
End If
esc = Mid(str, idx, 1)
If esc <> "u" Then
Select Case esc
Case """" char = """"
Case "" char = ""
Case "/" char = "/"
Case "b" char = b
Case "f" char = f
Case "n" char = n
Case "r" char = r
Case "t" char = t
Case Else Err.Raise 8732,,"Invalid escape"
End Select
idx = idx + 1
Else
char = ChrW("&H" & Mid(str, idx + 1, 4))
idx = idx + 5
End If
chunks.Add chunks.Count, char
Loop
ParseString = Join(chunks.Items, "")
End Function
Private Function SkipWhitespace(ByRef str, ByVal idx)
Do While idx <= Len(str) And _
InStr(Whitespace, Mid(str, idx, 1)) > 0
idx = idx + 1
Loop
SkipWhitespace = idx
End Function
End Class
Set wshnamed=wscript.arguments.named
strGraphID = wshnamed.item("graphid")
strPicSavePath = wshnamed.item("PicSavePath")
strCookies = wshnamed.item("Cookies")
Set fso = CreateObject("Scripting.FileSystemObject")
zabbix_url = "192.1.31.66"
zabbix_index = "http://" & zabbix_url & "/zabbix/index.php"
zabbix_webapi= "http://" & zabbix_url & "/zabbix/api_jsonrpc.php"
zabbix_username = "Admin"
zabbix_password = "zabbix"
Zabbix_cookie = GetZabbixCookie(zabbix_index,zabbix_username,zabbix_password)
If(Zabbix_cookie = "")Then
Wscript.Echo "Could not get Zabbix cookies, make sure your username and password is correct."
End If
Function GetAuthToken(url,username,password)
Set Winhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Winhttp.Open "POST", url
Winhttp.SetRequestHeader "Content-Type", "application/json-rpc"
If(cookie <> "")then
Winhttp.SetRequestHeader "Cookie",cookie
End If
Winhttp.Send "{""jsonrpc"": ""2.0"", ""method"": ""user.login"", ""params"":{""user"": """&username&""",""password"": """&password&"""}, ""id"": 0}"
Set json = New VbsJson
GetAuthToken = json.Decode(Winhttp.ResponseText)("result")
End Function
Function GetDaySecond(Day)
GetDaySecond = Day * 24 * 3600
End Function
Function GetGraphid(url,AuthCode,itemid)
Set Winhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Winhttp.Open "POST", url
Winhttp.SetRequestHeader "Content-Type", "application/json-rpc"
Winhttp.Send "{""jsonrpc"": ""2.0"",""method"": ""graphitem.get"",""params"": {""output"": ""extend"",""expandData"": 1,""itemids"": """&itemid&"""},""auth"": """&AuthCode&""",""id"": 1}"
Set json = New VbsJson
GetGraphid = json.Decode(WinHttp.ResponseText)("result")(0)("graphid")
End Function
Function GetHostid(url,AuthCode,zabbix_agent_hostname)
Set Winhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Winhttp.Open "POST", url
Winhttp.SetRequestHeader "Content-Type", "application/json-rpc"
If(cookie <> "")then
Winhttp.SetRequestHeader "Cookie",cookie
End If
Winhttp.Send "{""jsonrpc"": ""2.0"",""method"": ""host.get"",""params"": {""output"": ""extend"",""filter"": {""host"": """&zabbix_agent_hostname&"""}},""auth"": """&AuthCode&""",""id"": 1}"
Set json = New VbsJson
GetHostid = json.Decode(Winhttp.ResponseText)("result")(0)("hostid")
End Function
Function GetItemid(url,AuthCode,hostid,zabbix_item_key)
Set Winhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Winhttp.Open "POST", url
Winhttp.SetRequestHeader "Content-Type", "application/json-rpc"
If(cookie <> "")then
Winhttp.SetRequestHeader "Cookie",cookie
End If
Winhttp.Send "{""jsonrpc"": ""2.0"",""method"": ""item.get"",""params"": {""output"": ""extend"",""hostids"": """ &hostid & """,""search"":{""key_"": """ & zabbix_item_key & """},""sortfield"": ""name""},""auth"": """&AuthCode&""",""id"": 1}"
GetItemid = GetMid(Winhttp.ResponseText,"{""itemid"":""",""",""",1)
End Function
Function GetMid(strText, strFormer, strLater,intStartLocation)
Dim FormerLocation
Dim LaterLocation
Dim PFormerLocation
Dim PLaterLocation
FormerLocation = InStr(intStartLocation, strText, strFormer)
If (FormerLocation <> 0) Then
FormerLocation = FormerLocation + Len(strFormer)
LaterLocation = InStr(FormerLocation, strText, strLater)
If (LaterLocation <> 0) Then
GetMid = Mid(strText, FormerLocation, LaterLocation - FormerLocation)
Exit Function
End If
End If
GetMid = ""
End Function
Function GetZabbixCookie(zabbix_index,username,password)
Set Winhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Winhttp.Open "POST", zabbix_index
Winhttp.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
Winhttp.Send "name=" & username & "&password=" & password & "&autologin=1&enter=Sign+in"
GetZabbixCookie = "zbx_sessionid=" & GetMid(winhttp.GetAllResponseHeaders,"zbx_sessionid=",";",1) & ";"
End Function
Sub DownloadZabbixPic(url,strPath,cookie)
Set Winhttp = CreateObject("WinHttp.WinHttpRequest.5.1")
Winhttp.Open "GET", url
Winhttp.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
If(cookie <> "")then
Winhttp.SetRequestHeader "Cookie",cookie
End If
Winhttp.Send
Set sGet = CreateObject("ADODB.Stream")
sGet.Mode = 3
sGet.Type = 1
sGet.Open()
sGet.Write(Winhttp.ResponseBody)
sGet.SaveToFile strPath
End Sub
AuthCode = GetAuthToken(zabbix_webapi,zabbix_username,zabbix_password)
If AuthCode = "" Then
Wscript.Echo "Could not get AuthCode."
Wscript.Quit
End If
CurrentFolder = fso.getfolder(".")
CSV_Path = CurrentFolder&"list.csv"
If (fso.fileExists(CSV_Path)=0) Then
Wscript.Echo "Could not find " & CSV_Path & "."
Wscript.Quit
End If
set csv_file = fso.opentextfile(CSV_Path)
csv_text = csv_file.readall
csv_file.close
PicSaveDir = CurrentFolder&""&replace(date(),"/","")
If (fso.folderExists(PicSaveDir)=0) Then
fso.createfolder(PicSaveDir)
End If
CSV_ReadLine = split(csv_text,vbCrlf)
for i = 1 to ubound(CSV_ReadLine) step 1
CSV_ReadCol = split(CSV_ReadLine(i),"!")
Zabbix_agent_host = CSV_ReadCol(0)
ItemKey = CSV_ReadCol(1)
PicSaveItemDir = PicSaveDir & "" & Left(ItemKey,4)
If (fso.folderExists(PicSaveItemDir)=0) Then
fso.createfolder(PicSaveItemDir)
End if
PicSavePath = PicSaveItemDir & "" & Zabbix_agent_host & ".png"
Hostid = GetHostid(zabbix_webapi,AuthCode,Zabbix_agent_host)
If (Hostid = "") Then
Wscript.echo "Hostid is empty. Current host is: " & Zabbix_agent_host
Else
ItemID = GetItemid(zabbix_webapi,AuthCode,Hostid,ItemKey)
If (Itemid = "") Then
Wscript.echo "Itemid is empty. Current host is: " & Zabbix_agent_host & ". Current item is: "&ItemKey
Else
Graphid = GetGraphid(zabbix_webapi,AuthCode,itemid)
If (graphid = "") Then
Wscript.echo "Graphid is empty. Current host is: " & Zabbix_agent_host
Else
If (fso.fileExists(PicSavePath)) Then
Wscript.echo "PNG alreadly exist. " & "Current host is: " & Zabbix_agent_host & ". Current item is: "&ItemKey
Else
DownloadZabbixPic "http://" & zabbix_url & "/zabbix/chart2.php?graphid=" & Graphid & "&period=" & GetDaySecond(30),PicSavePath,Zabbix_cookie
Wscript.Echo Zabbix_agent_host & " " & ItemKey &" successfully save as " & PicSavePath
End if
End If
End If
End If
Next
我来介绍一下此脚本的使用方法。首先开始的第一段设置了这个脚本会在运行出错时,不会退出程序转而继续运行。
然后是一个json的类,用于在vbs中分析json,不过这个json类有点问题,只要json一复杂,就会分析出错。所以如果json很长我会使用另外一个命令去获取json的值。
这个脚本的正常运行还需要一个csv的列表文件来修改我们需要检查的host主机的名字。而且这次我添加了item的项,决定检查host的什么item的续表。其中csv默认的分隔符为“,”,但是我们要改成“!”。因为Itemkey中可能包含逗号,这样子脚本会误把itemkey的值给拆分了。如果同一个主机你需要两种不同的item的图表,你需要写两行。第一行默认是注释,所以是无视掉的。csv文件可以在excel里生成并且修改。也可以在任何的文本编辑器中进行编辑。
这个csv的写法如下:
zabbixAgentHost!itemkey
Zabbix server!system.cpu.utils[,avg]
Zabbix server!vm.memory.useage
这个脚本中变动的部分包括,zabbix的IP地址(对应变量:zabbix_url)、用户名(zabbix_username)、密码(zabbix_password)。还有图表生成php的参数。
图片会默认生成在vbs所在的路径处,先生成一个由今天日期产生的文件夹,再以item名的前4位创建子文件夹,然后图片会放在对应的文件夹中。图片的明明规则是zabbix_agent_hostname的值加.png后缀名。如果图片已经存在,会在运行的时候提示文件已存在,但是不会覆盖已存在的图片。
以下是脚本中function的解释:
GetAuthToken(url,username,password)
通过用户名密码获取webapi authtoken
GetGraphid(url,AuthCode,itemid)
通过itemid获取graphid
GetHostid(url,AuthCode,zabbix_agent_hostname)
通过authcode和zabbix_agent_hostname获取hostid
GetItemid(url,AuthCode,hostid,zabbix_item_key)
通过hostid和itemkey获取itemid
GetMid(strText, strFormer, strLater,intStartLocation)
改编自精易模块的“文本_取中间文本”主要是取出两个字符串中间的结果,用于json不能被正常解析的时候通过文本的形式获取json的值。
GetDaySecond(Day)
计算N天的秒数,这个值用于后期给chart2.php生成表格的时候,提供一个参数的值。
GetZabbixCookie(zabbix_index,username,password)
通过zabbix的用户名和密码来获取cookie。cookie是成功下载到正确的图片的必要条件!
DownloadZabbixPic(url,strPath,cookie)
事实上这个函数并不仅仅能够获取图片,它是将网页返回的内容以二进制的形式保存为一个文件。其中还能使用到cookie。
脚本会读取list.csv文件中的信息(一定要同目录下有一个list.csv文件)然后将它们转换成图片。
注意的是图片生成的参数,你可以搜索脚本中的DownloadZabbixPic来看到zabbix生成图表的参数。主要有四个参数比较重要,stime代表起始时间用法是(yyyyMMDDhhmmss)不填会默认为过去对应时长到现在当前的数据,period代表时长单位是秒可以用getdaysecond来计算出秒数,默认是30天的数据,如果想改可以自己修改,不填这个数据也是默认30天数据。还有两个值是weight和height,这两个值分别控制图片的长和宽。不填也可以。
请在cmd里使用cscript去运行这个vbs,否则报错和提示信息会以信息框的形式出现。你会被不停的弹框烦死。你可以将cscript的命令重定向到一个log里,作为这个脚本的日志文件。
以上是关于Zabbix之通过Graphid下载图片——第二版的主要内容,如果未能解决你的问题,请参考以下文章
Python 核心编程 (第二版) 中文高清pdf版110M高清下载