頻道欄目
首頁 > 程序開發 > Web開發 > Python > 正文
基于zabbix用Python寫一個運維流量氣象圖
2016-03-09 09:16:29           
收藏   我要投稿

前言:同事問我,你寫運維平臺最先寫哪一部分?好吧,還真把我問倒了,因為這是在問最應該放在放在第一位的東西~作為一個工作不足兩年,運維不足一年的新手來說,還真不敢妄下評論,其實按照我的思路,覺得最重要的部分肯定是故障處理,報警,但是這一塊怎么寫?怎么說?肯定不能重復造輪子了,不過我最想寫的是報表系統,思路是有的,但是一直耽擱了,詳情參考https://youerning.blog.51cto.com/10513771/1708925。

好吧,在回到那個問題,應該先寫哪個部分。我沒回答,反問他了。

他說,應該是運維氣象圖,這張圖上有各個節點的位置,并且標注出流量情況,如果我們在服務器發生故障的時候發現其中一個節點流量過高或者過低,或者一些其他指標,我們在一定程度上可以快速的地位故障的位置。注:cacti,zabbix似乎是有這個插件的,不過不是那么好看,或者有一定局限性,我也沒調查過,反正不想用。

然后,我被上了一課, 那么為毛類似這樣的應用或者框架之類的(除了上面說的兩個插件,本人暫時沒有Google或百度到,如果你知道抨擊一下我唄,當然,最好是Python開發的,我好自定義一下),他說,收費的軟件有~~~

 

首先瞧瞧我花了幾天鼓搗出來的Beta版本吧:

wKioL1bcIAqwbO3OAACOSx9nVX4089.png

現在,正題~~~

如果你不會python就收藏著以后看吧,當下就看看思路吧。

如果你會python就在涉獵一下JS吧,比如AngularJS,D3JS什么的,不過可以

如果你什么都不會,希望能激發你的興趣。

如果你是大神,還執意要看就忽略我代碼中的一些寫的不優雅,不好看的地方吧T_T

題外話:話說,有什么覺得比較實用的功能是需要收費的,或者一些想法需要實現的可以Q我,我們把它實現了(僅限于大概一周以內能寫完的,特別有意思的來說)

授人以魚不如授人以漁嘛,主要兩部分,一部分思路,一部分代碼講解

 

(一)

思路

Q:數據來源,通過寫客戶端?

A:當然不,nagios,cacti,zabbix什么的不是有一大堆么,為毛還要自己寫,而且還不一定寫的比別人好,美其名曰不愿重復造輪子~~~根據自己情況選擇吧,這里就選的通過zabbix的API作為數據來源。

Q:用什么web框架?

A:用Flask,很喜歡一句從網上看來的評論django的話,上它的人很多,喜歡它的很少,再者,我實在不想去配置什么配置文件,以及幫我創建一大堆文件(當然也可以不需要),再再者,我的功能不需要太多,再再再者,flask的官方文檔寫得太棒了~~~

 

然后瞧瞧我們寫什么,完成什么~

 

代碼實現:

web框架flask

功能頁面:

頁面一:展示頁面(bootstrap提供樣式效果,AngularJS實時數據查詢并刷新,為毛不用jQuery?因為jQuery我不會T_T)

頁面二:數據查詢接口

代碼文件:

一:用于查詢數據的py模塊 x 1

二:用于提供web界面的py文件 x 1

三:html模板文件 x 3

四:js文件 x 2

 

注:其實個人不建議用模板渲染直接把數據渲染到展示頁面上去,雖然這樣不用寫js了,但是寫到后面,你就難過了,所以前臺后臺分離吧,這里也是前臺后臺分離,前臺bootstrap加AngularJS~~~

 

(二)

代碼

zabbix數據獲取

獲取zabbix數據可參考:https://youerning.blog.51cto.com/10513771/1740152第三部分~~

所以直接放代碼吧~~

#coding=utf-8
importjson
importrequests
frompprintimportpprint
fromosimportpath
###zabbixapi訪問地址
zabbix_pre="https://10.10.102.88/zabbix/"
zabbix_url=zabbix_pre+"/api_jsonrpc.php"
###用戶名密碼
user=""
passwd=""
###這里只查詢進出口流量,所以只有下面兩個關鍵字,后面可能會查詢一些其他的~~~
net_in="net.if.in[eth0]"
net_out="net.if.out[eth0]"
###構造post請求提交的數據
auth_data=json.dumps(
{
"jsonrpc":"2.0",
"method":"user.login",
"params":{
"user":"%s"%user,
"password":"%s"%passwd
},
"id":0
})
###http頭部信息,zabbix要求的
headers={
'content-type':'application/json',
}
###構造一個返回查詢hostid的json數據,函數是一等公民~~~
defhost_data(auth):
data=json.dumps(
{
"jsonrpc":"2.0",
"method":"host.get",
"params":{
"output":["hostid","host"],
"search":{"host":""}
},
"auth":"%s"%auth,
"id":1,
})
returndata
###如上,查詢hostid
defhost_data_search(auth,search):
data=json.dumps(
{
"jsonrpc":"2.0",
"method":"host.get",
"params":{
"output":["hostid","name"],
"search":{"host":search}
},
"auth":"%s"%auth,
"id":1,
})
###如上,查詢itemid
defitem_data_filter1(auth,hostid,filters):
data=json.dumps(
{
"jsonrpc":"2.0",
"method":"item.get",
"params":{
"output":["itemid"],
"hostids":"%s"%hostid,
"search":{
"key_":filters
}
},
"auth":"%s"%auth,
"id":1,
})
returndata
###如上,查詢item的所有信息,hostname,itemid一大堆
defitem_data_filter2(auth,hostid,filters):
data=json.dumps(
{
"jsonrpc":"2.0",
"method":"item.get",
"params":{
"output":"extend",
"hostids":"%s"%hostid,
"filter":{
"name":filters
},
"sortfield":"name"
},
"auth":"%s"%auth,
"id":1
})
returndata
###如上,獲取最新監控值
defhistory_data(auth,itemid,limit,his=0):
data=json.dumps(
{
"jsonrpc":"2.0",
"method":"history.get",
"params":{
"output":"extend",
"history":his,
"sortfield":"clock",
"sortorder":"DESC",
"itemids":"%s"%itemid,
"limit":limit
},
"auth":"%s"%auth,
"id":1,
})
returndata
###構造獲取zabbix驗證id,為了反復操作,當然封裝成函數
defgetauth(zabbix_url,auth_data,headers):
auth_ret=requests.post(zabbix_url,data=auth_data,headers=headers)
auth_id=auth_ret.json()["result"]
returnauth_id
###將所有結果保存成本地之間,結果包括,主機名(這里指zabbix上的命名),hostid,出入口的itemid
defsavefile():
host_ret=requests.post(zabbix_url,data=host_data(auth_id),headers=headers)
host_ret=host_ret.json()["result"]

###這里請根據實際情況設定,比如包括nginx集群,mysql集群,tomcat集群,如下
json_all={}
json_all["nginx_cluster"]={}
json_all["tomcat_cluster"]={}
json_all["mysql_cluster"]={}
forhostinhost_ret:
hostid=host["hostid"]
hostname=host["host"]
item_ret=requests.post(zabbix_url,data=item_data_filter1(auth_id,hostid,"net.if"),\
headers=headers)
item_ret=item_ret.json()["result"]
#pprint(item_ret)
item_in=item_ret[0]["itemid"]
item_out=item_ret[1]["itemid"]
""""這里如上,根據實際情況設定
if"nginx"inhostname:
json_all["nginx_cluster"][hostname]=[hostid,item_in,item_out]
elif"tomcat"inhostname:
json_all["tomcat_cluster"][hostname]=[hostid,item_in,item_out]
elif"mysql"inhostname:
json_all["mysql_cluster"][hostname]=[hostid,item_in,item_out]
else:
pass"""
#pprint(json_all)
fp=open("clusters.json","w")
fp.write(json.dumps(json_all))
fp.close()
###然后通過itemid獲取最新的監控值
defgethist(auth_id,itemid,limit,outtype=3):
whilenotpath.isfile("clusters.json"):
savefile()
history_ret=requests.post(zabbix_url,data=history_data(auth_id,itemid,limit,outtype),\
headers=headers)
#printhistory_ret.json()
iflen(history_ret.json()["result"])==0:
return0
else:
history_ret=history_ret.json()["result"][0]
#pprint(history_ret["value"])
returnhistory_ret["value"]
###然后通過集群名獲取整個集群的總和監控值
defgethist_cluster(auth_id,cluster_name,opt):
clsname=cluster_name
opt=opt
auth_id=getauth(zabbix_url,auth_data,headers)
whilenotpath.isfile("clusters.json"):
savefile()
cluster_file=json.load(open("clusters.json","r"))
net_list={"in":1,"out":2}
ifclsnameincluster_file.keys()andoptinnet_list.keys():
sum=0
cls=cluster_file[clsname]
inf=net_list[opt]
forhostincls:
itemid=cls[host][inf]
his_ret=int(gethist(auth_id,itemid,1,3))
sum=sum+his_ret
#printfloat(sum)/float(1024)
returnfloat(sum)/float(1024)
auth_id=getauth(zabbix_url,auth_data,headers)
#gethist(auth_id,25919,1,3)
printgethist_cluster(auth_id,"mysql_cluster","in")

 

然后將上面的代碼保存為getsource.py文件用作模塊導入,之所以不將所有py代碼不寫在一起也是為了更好看,更容易反復使用。

注:如果主機多的話會很慢吧~~~因為我沒有寫并發

然后是flask部分的web代碼

#coding:utf-8
fromflaskimportFlask,jsonify,render_template
###flask的插件,用的restful作為提供api
fromflask.extimportrestful
###從上面的那個py文件導入我們需要的函數
fromgetsourceimportgethist,getauth,gethist_cluster,savefile
importjson
fromosimportpath
app=Flask(__name__)
api=restful.Api(app)
###zabixurl
zabbix_pre="https://10.10.102.88/zabbix/"
zabbix_url=zabbix_pre+"/api_jsonrpc.php"
###usernameandpasswd
user=""
passwd=""
###authdata
auth_data=json.dumps(
{
"jsonrpc":"2.0",
"method":"user.login",
"params":{
"user":"%s"%user,
"password":"%s"%passwd
},
"id":0
})
###headers
headers={
'content-type':'application/json',
}
###根據實際情況設定,這里給每個集群加了個id,用于排序,實際集群參考上面代碼,這里也以nginx_cluser,tomcat_cluster,mysql_cluster為例
cluster_id={
"nginx_cluster":1,
"tomcat_cluster":2,
"mysql_cluster":7
}
classMyApi(restful.Resource):
defget(self,name,opt):
cls=name
opt=opt
auth_id=getauth(zabbix_url,auth_data,headers)
whilenotpath.isfile("clusters.json"):
savefile()
cluster_file=json.load(open("clusters.json","r"))
net_list={"in":1,"out":2}
ifclsincluster_file.keys()andoptinnet_list.keys():
sum=0
cls=cluster_file[cls]
inf=net_list[opt]
forhostincls:
itemid=cls[host][inf]
his_ret=int(gethist(auth_id,itemid,1,3))
sum=sum+his_ret
returnfloat(sum)/float(1024)
elifcls=="cluster_all"andopt=="traffic":
keys=cluster_file.keys()
cls_ret={}
cls_lis=[]
forkeyinkeys:
dic={}
dic["name"]=key
dic["id"]=cluster_id[key]
dic["in"]=gethist_cluster(auth_id,key,"in")
dic["out"]=gethist_cluster(auth_id,key,"out")
cls_lis.append(dic)
cls_ret["ret"]=cls_lis
returnjsonify(cls_ret)
elifcls=="cluster_all"andopt=="list":
returnjsonify(cluster_file)
elifclsincluster_id.keys()andopt=="list":
ret=cluster_file[cls]
returnjsonify(ret)
else:
return"None"
api.add_resource(MyApi,"/api//")
@app.route("/")
defhello():
return"Helloworld你好"
@app.route("/weathermap")
@app.route("/weathermap/")
defweathermap(name=None):
name=name
ifname=="all":
returnrender_template("weathermap_all.html")
elifname=="list":
returnrender_template("weathermap_list.html")
elifname=="plot":
returnrender_template("weathermap_plot.html")
else:
returnrender_template("weathermap_all.html")
app.debug=True
app.run(host="0.0.0.0")

再是html模板文件

layout.html



<scriptsrc="/static/js/angular.min.js"></script><scriptsrc="/static/js/d3.v3.min.js"></script>Beta

運維流量表

{%blockbody%}{%endblock%}

weathermap_all.hml

{%extends"layout.html"%}
{%blockbody%}

集群流量一覽表

ID 集群名 出口流量(KB/s) 入口流量(KB/s) <scriptsrc="/static/js/jquery-1.11.3.min.js"></script><scriptsrc="/static/js/bootstrap.min.js"></script><scriptsrc="/static/js/netdata.js"></script><scriptsrc="/static/js/cls_svg.js"></script>{%endblock%}

weathermap_list.html

{%extends"layout.html"%}
{%blockbody%}

集群流量一覽表

ID 集群名 出口流量(KB/s) 入口流量(KB/s) <scriptsrc="/static/js/jquery-1.11.3.min.js"></script><scriptsrc="/static/js/bootstrap.min.js"></script><scriptsrc="/static/js/netdata.js"></script>{%endblock%}

weathermap_plot.html

{%blockbody%}

集群拓撲圖

<scriptsrc="/static/js/jquery-1.11.3.min.js"></script><scriptsrc="/static/js/bootstrap.min.js"></script><scriptsrc="/static/js/cls_svg.js"></script>{%endblock%}

最后js文件

netdata.js

varapp=angular.module('myApp',[]);
app.controller('myCtrl',function($scope,$http){
$http.get("/api/cluster_all/traffic")
.success(function(response){$scope.names=response.ret;});
});

cls_svg.js

###假如是nginx_cluster,tomcat_cluster,mysql_cluster
varnodes=[{name:"nginx_cluster"},{name:"tomcat_cluster"},
{name:"mysql_cluster"},{name:"Internet"}];
varedges=[{source:0,target:1},{source:0,target:2},
{source:1,target:3},{source:3,target:0}];
###上面是0到1,0到2,1到3,3到0的連線,數字分別對應上面的nodes里的name順序
varwidth=500;
varheight=500;
varsvg=d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
varforce=d3.layout.force()
.nodes(nodes)//指定節點數組
.links(edges)//指定連線數組
.size([width,height])//指定范圍
.linkDistance(200)//指定連線長度
.charge([-400]);//相互之間的作用力
force.start();//開始作用
console.log(nodes);
console.log(edges);
//添加連線
varsvg_edges=svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1);
varcolor=d3.scale.category20();
//添加節點
varsvg_nodes=svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r",20)
.style("fill",function(d,i){
returncolor(i);
})
.call(force.drag);//使得節點能夠拖動
//添加描述節點的文字
varsvg_texts=svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill","black")
.attr("dx",20)
.attr("dy",8)
.text(function(d){
returnd.name;
});
force.on("tick",function(){//對于每一個時間間隔
//更新連線坐標
svg_edges.attr("x1",function(d){returnd.source.x;})
.attr("y1",function(d){returnd.source.y;})
.attr("x2",function(d){returnd.target.x;})
.attr("y2",function(d){returnd.target.y;});
//更新節點坐標
svg_nodes.attr("cx",function(d){returnd.x;})
.attr("cy",function(d){returnd.y;});
//更新文字坐標
svg_texts.attr("x",function(d){returnd.x;})
.attr("y",function(d){returnd.y;});
});

注:我會告訴你我直接去copy來的么,d3js還在鉆研ing,所以僅作demo用

然后是目錄結構

├──clusters.json

├──getsource.py

├──myapp.py

├──static

│├──css

││└──bootstrap.min.css

│└──js

│├──angular.min.js

│├──bootstrap.min.js

│├──cls_svg.js

│├──d3.v3.min.js

│├──jquery-1.11.3.min.js

│└──netdata.js

└──templates

├──layout.html

├──weathermap_all.html

├──weathermap_list.html

└──weathermap_plot.html

 

如果都完成了,整個項目是下面這樣的

注:bootstrap,jquery,angularjs這些css,js文件百度Google下載吧

 

API:

查詢jjjr2集群列表 https://IP:5000/api/cluster_all/list

查詢jjjr2集群流量進出口情況 https://IP:5000/api/cluster_all/traffic

查詢單個集群列表(如tomcat集群) https://IP:5000/api/tomcat_cluster/list

查詢單個集群流量進出口情況 https://IP:5000/api/tomcat_cluster/in 其中in,out分別代表入口,出口流量

查詢單個主機流量進出口情況(暫不能提供)

比如

wKiom1bcI8DSG4q6AAEjzxhYrnw474.png

訪問:http//IP:5000/weathermap/list,僅訪問列表

訪問:http//IP:5000/weathermap/plot,僅訪問拓撲圖

訪問:http//IP:5000/weathermap或者http//IP:5000/weathermap/all 查看列表與拓撲圖在同一個頁面

比如

 

wKiom1bcI9DDAe3xAAGQTMl9HE4742.png

wKioL1bcJFDTPJ97AADJxt01lS8053.png

存在問題:

1:mysql集群中包括了poms-mysql(后面更新)

2:拓撲圖并不理想

3:頁面不能實時刷新,通過在html頁面加入

可頁面自動刷新,可是不優雅,所以并未添加

4:并未設置閾值已區別各個集群健康情況

5:并沒有考慮中間件之類,總而言之,現在很粗糙~~

總結:我們將獲取zabbix數據的代碼部分抽離出來做成一個模塊,這樣就能分工明確,也為了讓代碼顯得不是那么龐大很難看,然后web方面主要提供功能,API以及數據展示,通過API我們可以將數據反復利用,并且有很好的兼容性,web的展示當然不能少了bootstrap,一個多漂亮的樣式庫,不用自己設置css,然后數據操作通過AngularJS,前臺通過AngularJS去調用自身提供的API以獲取數據,然后填充,最后拓撲圖用強大的D3JS,這是做前端的同事推薦的~~

后記:我一直想寫個項目,不是很大的項目,因為這樣很快的就能寫完,太大的項目要寫太久太久~基于的工作閱歷還不夠感受到的痛點并不多,所以想到的點子并不多,并且也找到了很好的工具了,如果誰來倆點子,我幫你寫出來唄~當然了,希望盈利的就算了,我想寫的項目都是能直接放到github上~~大家一起爽的那種,哈哈。

在一定意義上也是為了練手。

話說,我的從無到有寫一個運維APP系列如果這個月前,寫不完,也就也寫不完了~~~因為拖太長了,熱情要沒了~

點擊復制鏈接 與好友分享!回本站首頁
相關TAG標簽 氣象 流量
上一篇:Python基礎--快速改造的基礎知識介紹
下一篇:Python與硬件學習筆記:藍牙(二)
相關文章
圖文推薦
點擊排行

關于我們 | 聯系我們 | 廣告服務 | 投資合作 | 版權申明 | 在線幫助 | 網站地圖 | 作品發布 | Vip技術培訓 | 舉報中心

版權所有: 紅黑聯盟--致力于做實用的IT技術學習網站

加拿大28火车判定方法