接上篇《授之以渔-运维平台发布模块一(Jenkins篇)》,今天介绍下结合着Saltstack的MasterEven,用来做发布系统的结果监控。

一、Event介绍

SaltStack 0.10版本中, 新增了Event系统, 官方在 Release Notes 对其描述如下:

The Salt Master now comes equipped with a new event system. This event system has replaced some of the back end of the Salt client and offers the beginning of a system which will make plugging external applications into Salt. The event system relies on a local ZeroMQ publish socket and other processes can connect to this socket and listen for events. The new events can be easily managed via Salt's event library. 同时官方也在 Salt Event系统 页面中提供了监听event的例子程序, 基于其进行下Event系统学习. 我们所需要的就是监听event,然后把结果存储到Mysql,Redis之类,以便于我们后续进行处理。

二、Event代码:

import salt.utils.event
import redis

def redis_conect_db6():
    try:
        r = redis.StrictRedis('172.18.11.126', 6379, 6)
    except:
        pass
    return r



event = salt.utils.event.MasterEvent('/var/run/salt/master')
for data in event.iter_events(full=True):
	r = redis_conect_db6()
	ret = data['data']
	if 'salt/job/' in data['tag']:
		if ret.has_key("id") and ret.has_key("return") and ret.has_key("fun") :
			if ret['fun'] != 'mine.update':
				if len(ret['fun_args']) >0 and 'wwwlogs' not in ret['fun_args'][0]:
					print data
					print "====================================================="
					if ret['fun'] == 'pkg.get_repo' and ret['return'] != {}:
							r.set('%s:%s-%s'%(ret['return']['name'],ret['id'],ret['fun']), ret)
					elif ret['fun'] == 'pkg.upgrade_available' :
							r.set('%s:%s-%s'%(ret['fun_args'][0],ret['id'],ret['fun']), ret)
					elif ret['fun'] == 'pkg.install' and ret['return'] != {}:
							r.set('%s:%s-%s'%(ret['fun_args'][0],ret['id'],ret['fun']), ret)


这段event脚本的大体实现了过滤一些非必要信息,把fun中包含pkg.get_repo(验证Yum是否存在),pkg.upgrade_available(验证Yum是否更新),pkg.install(安装)存储到Redis中。然后我只需要去Redis中取出数据,并通过highcharts.js汇出图即可。

三、后端处理Redis数据绘图代码:

def release_status(request):
    request.GET:
        svn_name = request.GET[]
        Svn_name = Svn.objects.get(= svn_name)
        r = redis_conect_db6()
        svn_list = []
        nodes = [{:,:,:,:[],:% (Svn_name.svn_name)}]
        svn_host (Svn_name.svn_hosts).split():
            area_dict1 = {}
            area_dict2 = {}
            area_dict3 = {}
            svn_list_dict = {}
            area_dict1[] = area_dict1[] = (Svn_name.svn_hosts).split().index(svn_host)
            area_dict1[] = % (svn_host)
            area_dict1[] = [{:,:}]
            area_dict1[] = nodes.append(area_dict1)
            area_dict2[] = area_dict2[] = (Svn_name.svn_hosts).split().index(svn_host)
            area_dict2[] = % (svn_host)
            area_dict2[] = [{:,:(area_dict2[])}]
            area_dict2[] = nodes.append(area_dict2)
            area_dict3[] = area_dict3[] = (Svn_name.svn_hosts).split().index(svn_host)
            area_dict3[] = % (svn_host)
            area_dict3[] = [{:,:(area_dict3[])}]
            area_dict3[] = nodes.append(area_dict3)
            :
                (Svn_name) r.get(% (svn_name, svn_host,)):
                    nodes[] = {:,:,:,:[],:% (Svn_name.svn_name)}

                    nodes[][] == :
                        area_dict1[] = :
                            json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[] == :
                                area_dict1[] = :
                                area_dict1[] = :
                            area_dict1[] = area_dict1[] = area_dict1[] = (Svn_name.svn_hosts).split().index(svn_host)
                        area_dict1[] = % (svn_host)
                        area_dict1[] = [{:,:}]
                        svn_list_dict[] = svn_host
                        svn_list_dict[] = Svn_name
                        svn_list_dict[] = Svn_name.svn_no

                        :
                            svn_list_dict[] = json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[]
                        :
                            svn_list_dict[] = nodes.append(area_dict1)
                        ,nodes[][]
                    :
                        area_dict1[] = nodes[][] == :
                        area_dict2[] = :
                            json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[] == :
                                area_dict2[] = :
                                area_dict2[] = :
                            area_dict2[] = area_dict2[] = area_dict2[] = (Svn_name.svn_hosts).split().index(svn_host)
                        area_dict2[] = % (svn_host)
                        area_dict2[] = [{:,:(area_dict2[])}]

                        :
                            svn_list_dict[] = json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[]
                        :
                            svn_list_dict[] = nodes.append(area_dict2)
                    :
                        area_dict2[] = nodes[][]  == :
                        area_dict3[] = :
                            json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[] == :
                                json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[][% svn_name][] != :
                                    release_old = json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[][% svn_name][].split()[]
                                :
                                    release_old = :
                                    release_new = json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[][% svn_name][].split()[]
                                :
                                    release_new = ((release_old)+== (release_new)) ((release_new) == (Svn_name.svn_no)):
                                    area_dict3[] = (release_old) == (release_new):
                                    area_dict3[] = :
                                    area_dict3[] = :
                                area_dict3[] = :
                            area_dict3[] = area_dict3[] = area_dict3[] = (Svn_name.svn_hosts).split().index(svn_host)
                        area_dict3[] = % (svn_host)
                        area_dict3[] = [{:,:(area_dict3[])}]
                        :
                            svn_list_dict[] = json.loads(r.get(% (svn_name, svn_host, )).replace(, ).replace(,).replace(,))[]

                            svn_list_dict[] = release_new
                        :
                            svn_list_dict[] = nodes.append(area_dict3)
                    :
                        area_dict3[] = :
                    svn_list.append(svn_list_dict)
            err:
                ,err
        render_to_response(,{:svn_list,:nodes},=RequestContext(request))
    :
        render_to_response(,=RequestContext(request))

这段代码大体实现了通过判断pkg.get_repo(验证Yum是否存在),pkg.upgrade_available(验证Yum是否更新),pkg.install(安装)返回的状态,来输出不同的status,前端会根据不同的status,绘制不同的颜色。

四、效果图:

Paste_Image.png

五、前端代码:

<script src="../assets/global/plugins/cmdb/highcharts.js" type="text/javascript"></script>

<script type="text/javascript">
    $(function () {
        var chart = new Highcharts.Chart({
            chart: {
                renderTo: 'container_drawing',
                events: {
                    load: drawing
                }
            },
            title: {
                text: ''
            }
        });
    });
    /* 画图自定义函数 */
    function drawing(){
        // Draw the flow chart
        var ren = this.renderer;
        var rightArrow = ['M', 0, 0, 'L', 100, 0, 'L', 95, 5, 'M', 100, 0, 'L', 95, -5];
        var colors = ['#2570a1','#d71345','#decb00','#1d953f'];//四种状态对应的颜色:未启动,告警,正在运行,完成

        // 分割线1
        ren.path(['M', 270, 40, 'L', 270, 600]).attr({'stroke-width': 3,stroke: 'silver',dashstyle: 'dash'}).add();
        // 分割线2
        ren.path(['M', 520, 40, 'L', 520, 600]).attr({'stroke-width': 3,stroke: 'silver',dashstyle: 'dash'}).add();
        // 分割线3
        ren.path(['M', 770, 40, 'L', 770, 600]).attr({'stroke-width': 3,stroke: 'silver',dashstyle: 'dash'}).add();
        // 分割线4
//        ren.path(['M', 1020, 40, 'L', 1020, 600]).attr({'stroke-width': 3,stroke: 'silver',dashstyle: 'dash'}).add();
        // 分割线5
//        ren.path(['M', 1270, 40, 'L', 1270, 600]).attr({'stroke-width': 3,stroke: 'silver',dashstyle: 'dash'}).add();




        // 区域标题

        ren.label('包构建', 110,50).css({fontWeight: 'bold'}).add();
        ren.label('验证源', 370,50).css({fontWeight: 'bold'}).add();
        ren.label('验证包更新', 610, 50).css({fontWeight: 'bold'}).add();
//        ren.label('前置命令', 880, 50).css({fontWeight: 'bold'}).add();
        ren.label('项目包安装', 880, 50).css({fontWeight: 'bold'}).add();
//        ren.label('后置命令', 1380, 50).css({fontWeight: 'bold'}).add();

        // 遍历生成图形
        /**
            area:区域编码,值为:0,1,2,null.
            no:节点序号,值为:0,1,...,N,null.
            label:节点内容.
            status:节点状态值为:0为未执行,1为错误,2为执行中,3为执行完毕
            parea:父节点区域编码,值为:0,1,2,null.
            pno:父节点序号,值为:0,1,...,N,null.
        */
//        var nodes = [{"area":5,"status":2,"no":0,"parent":[{"parea":4,"pno":0}],"label":"md25-qnzs-app-13.youth.cn"},{"area":4,"status":3,"no":0,"parent":[{"parea":3,"pno":0}],"label":"md25-qnzs-app-13.youth.cn"},{"area":3,"status":3,"no":0,"parent":[{"parea":2,"pno":0}],"label":"md25-qnzs-app-13.youth.cn"},{"area":2,"status":3,"no":0,"parent":[{"parea":1,"pno":0},{"parea":1,"pno":1}],"label":"md25-qnzs-app-13.youth.cn"},{"area":1,"status":1,"no":0,"parent":[{"parea":0,"pno":0},{"parea":0,"pno":1},{"parea":0,"pno":3}],"label":"md25-qnzs-app-13.youth.cn"},{"area":1,"status":0,"no":1,"parent":[{"parea":0,"pno":1},{"parea":0,"pno":2}],"label":"md25-qnzs-app-13.youth.cn"},{"area":0,"status":0,"no":0,"parent":[],"label":"md25-qnzs-app-13.youth.cn"},{"area":0,"status":1,"no":1,"parent":[],"label":"md25-qnzs-app-13.youth.cn"},{"area":0,"status":2,"no":2,"parent":[],"label":"md25-qnzs-app-13.youth.cn"},{"area":0,"status":1,"no":3,"parent":[],"label":"md25-qnzs-app-13.youth.cn"}];
        var nodes = {% autoescape off %}{{ nodes}}{% endautoescape %}
        for(var i=0;i<nodes.length;i++){
            var node = nodes[i];
            ren.label(node.label, 30+node.area*250, 80+node.no*50).attr({fill: colors[node.status],stroke: '#f2eada','stroke-width': 2,padding: 5,r: 9,width:220,height:20}).css({color: 'white',fontSize:'xx-small',textAlign:'center',fontWeight: 'bold'}).add().shadow(true);
            for(var j=0;j<node.parent.length;j++){
                var parent = node.parent[j];
               ren.path(['M', 30+node.area*250, 75+node.no*50+20, 'L', 2+parent.parea*250+250+10, 75+parent.pno*50+20]).attr({'stroke-width': 2,stroke: '#4169E1'}).translate(0, 0).add();//
            }
        }
    }