接上篇《授之以渔-运维平台发布模块一(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数据绘图代码:
@login_required
@my_permissionVerify
def release_status(request):
if 'id' in request.GET:
svn_name = request.GET['id']
Svn_name = Svn.objects.get(svn_name = svn_name)
r = redis_conect_db6()
svn_list = []
nodes = [{"area":0,"status":2,"no":0,"parent":[],"label":"%s" % str(Svn_name.svn_name)}]
for svn_host in (Svn_name.svn_hosts).split(','):
area_dict1 = {}
area_dict2 = {}
area_dict3 = {}
svn_list_dict = {}
area_dict1["area"] = 1
area_dict1["no"] = (Svn_name.svn_hosts).split(',').index(svn_host)
area_dict1["label"] = "%s" % str(svn_host)
area_dict1["parent"] = [{"parea":0,"pno":0}]
area_dict1["status"] = 0
nodes.append(area_dict1)
area_dict2["area"] = 2
area_dict2["no"] = (Svn_name.svn_hosts).split(',').index(svn_host)
area_dict2["label"] = "%s" % str(svn_host)
area_dict2["parent"] = [{"parea":1,"pno":int(area_dict2["no"])}]
area_dict2["status"] = 0
nodes.append(area_dict2)
area_dict3["area"] = 3
area_dict3["no"] = (Svn_name.svn_hosts).split(',').index(svn_host)
area_dict3["label"] = "%s" % str(svn_host)
area_dict3["parent"] = [{"parea":2,"pno":int(area_dict3["no"])}]
area_dict3["status"] = 0
nodes.append(area_dict3)
"""排除同一个主机,多个项目"""
try:
if str(Svn_name) in r.get('%s:%s-%s' % (svn_name, svn_host,'pkg.get_repo')):
nodes[0] = {"area":0,"status":3,"no":0,"parent":[],"label":"%s" % str(Svn_name.svn_name)}
if nodes[0]['status'] == 3:
area_dict1["status"] = 2
try:
if json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.get_repo')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['success'] == "True":
area_dict1["status"] = 3
else:
area_dict1["status"] = 1
except:
area_dict1["status"] = 0
area_dict1["area"] = 1
area_dict1["no"] = (Svn_name.svn_hosts).split(',').index(svn_host)
area_dict1["label"] = "%s" % str(svn_host)
area_dict1["parent"] = [{"parea":0,"pno":0}]
svn_list_dict['host'] = svn_host
svn_list_dict['name'] = Svn_name
svn_list_dict["release_svn"] = Svn_name.svn_no
"""添加检测YUM源存在时间"""
try:
svn_list_dict['get_repo_time'] = json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.get_repo')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['_stamp_localtime']
except:
svn_list_dict['get_repo_time'] = ''
nodes.append(area_dict1)
print 'nodes,',nodes[1]['status']
else:
area_dict1["status"] = 0
if nodes[1]['status'] == 3:
area_dict2["status"] = 2
try:
if json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.upgrade_available')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['success'] == "True":
area_dict2["status"] = 3
else:
area_dict2["status"] = 1
except:
area_dict2["status"] = 2
area_dict2["area"] = 2
area_dict2["no"] = (Svn_name.svn_hosts).split(',').index(svn_host)
area_dict2["label"] = "%s" % str(svn_host)
area_dict2["parent"] = [{"parea":1,"pno":int(area_dict2["no"])}]
"""添加RPM检测更新时间"""
try:
svn_list_dict['upgrade_available'] = json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.upgrade_available')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['_stamp_localtime']
except:
svn_list_dict['upgrade_available'] = ''
nodes.append(area_dict2)
else:
area_dict2["status"] = 0
if nodes[2]['status'] == 3:
area_dict3["status"] = 2
try:
if json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.install')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['success'] == "True":
if json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.install')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['return']['%s'% svn_name]['old'] != '':
release_old = json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.install')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['return']['%s'% svn_name]['old'].split('-')[0]
else:
release_old = 0
try:
release_new = json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.install')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['return']['%s'% svn_name]['new'].split('-')[0]
except:
release_new = ''
if (int(release_old)+1 == int(release_new)) and (int(release_new) == int(Svn_name.svn_no)):
area_dict3["status"] = 3
elif int(release_old) == int(release_new):
area_dict3["status"] = 2
else:
area_dict3["status"] = 1
else:
area_dict3["status"] = 1
except:
area_dict3["status"] = 2
area_dict3["area"] = 3
area_dict3["no"] = (Svn_name.svn_hosts).split(',').index(svn_host)
area_dict3["label"] = "%s" % str(svn_host)
area_dict3["parent"] = [{"parea":2,"pno":int(area_dict3["no"])}]
"""添加RPM安装时间"""
try:
svn_list_dict['install'] = json.loads(r.get('%s:%s-%s' % (svn_name, svn_host, 'pkg.install')).replace("'", "\"").replace('True','"True"').replace('False','"False"'))['_stamp_localtime']
svn_list_dict["release_new"] = release_new
except:
svn_list_dict['install'] = ''
nodes.append(area_dict3)
else:
area_dict3["status"] = 0
else:
pass
svn_list.append(svn_list_dict)
except Exception as err:
print 'Release_status_err:',err
return render_to_response('myrelease/release_status.html',{'svn_list':svn_list,'nodes':nodes},context_instance=RequestContext(request))
else:
return render_to_response('myrelease/release_status.html',context_instance=RequestContext(request))
这段代码大体实现了通过判断
pkg.get_repo
(验证Yum是否存在),pkg.upgrade_available
(验证Yum是否更新),pkg.install
(安装)返回的状态,来输出不同的status,前端会根据不同的status,绘制不同的颜色。
四、效果图:
五、前端代码:
<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();//
}
}
}