<dfn id='5CAcZTYm'></dfn>

        <noscript id='5CAcZTYm'></noscript>

      1. 频道栏目
        首页 > 程序开发 > Web开发 > Python > 正文
        基于zabbix用Python写一个运维流量气象图
        2019-02-21 09:16:29           
        收藏   我要投稿

        前言:同事问我,你写运维平台最先写哪一部分?好吧,还真把我问倒了,因为这是在问最应该放在放在第一位的东西~作为一个工作不足两年,运维不足一年的新手来说,还真不敢妄下评论,其实按照我的思路,觉得最重要的部分肯定是故障处理,报警,但是这一块怎么写?怎么说?肯定不能重复造轮子了,不过我最想写的是报表系统,思路是有的,但是一直耽搁了,详情参考//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数据可参考://youerning.blog.51cto.com/10513771/1740152第三部分~~

        所以直接放代码吧~~

        #coding=utf-8
        importjson
        importrequests
        frompprintimportpprint
        fromosimportpath
        ###zabbixapi访问地址
        zabbix_pre="//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="//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集群列表 //IP:5000/api/cluster_all/list

        查询jjjr2集群流量进出口情况 //IP:5000/api/cluster_all/traffic

        查询单个集群列表(如tomcat集群) //IP:5000/api/tomcat_cluster/list

        查询单个集群流量进出口情况 //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技术学习网站