Django+redis+websocket+ansible实现自动化发布
之前已经发布过一个版本就是通过ajax的异步调用实现自动化发布 并将结果返回到web,但是这样有个缺点就是无法查看实时的过程,部署过程进行到什么地步了,并且报错的步骤是那一部分。所以我优化了下代码,使部署过程可视化的实时的显示在web端,我这里是使用redis作为中间件,当然你也可以选择mq作为中间件,都无所谓。
效果如图:当点击部署的时候会实时显示部署过程
首先是前端代码 我这里也只是写ajax调用部分,其余的自己考量
用于实时显示数据的ajax
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> <script type="text/javascript">//<![CDATA[ $(function () { $('#send_message').click(function () { //点击id号send_message 触发事件 $('#messagecontainer').html(""); //messagecontainer div id号用于显示数据 var socket = new WebSocket("ws://" + window.location.host + "/echo_once/"); socket.onopen = function () { console.log('WebSocket open');//成功连接上Websocket }; socket.onmessage = function (e) { console.log('message: ' + e.data);//打印服务端返回的数据 $('#messagecontainer').append('<p>' + e.data + '</p>'); }; }); }); //]]></script> 用于触发部署的异步ajax
$("#send_message").on('click',function(){ $.ajax({ url:"/Get_data", type:"POST", data:{ hosts:$("#hosts").val(), software:$("#software").val() }, success:function(data){ //以下是返回结果 当然可以选择不写 var obj=JSON.parse(data) $('#dict_result').html(JSON.stringify(obj.result.success)) $('#dict_result1').html(JSON.stringify(obj.result.failed)) $('#dict_result2').html(JSON.stringify(obj.result.unreachable)) $('#loading').hide(); } }) })
</script> views部分: Get_data负责执行ansible API接口 ansible负责渲染ansible部署页面 echo_once 负责实时输出结果 下面有一大部分是前面讲过的 这里就不提了,主要是echo_once 函数我这里提一下 funredis 模块:
import redis class RedisHelper: def __init__(self): self.__conn = redis.Redis(host='127.0.0.1') self.chan_sub = 'ansible' self.chan_pub = 'ansible' def public(self, msg): self.__conn.publish(self.chan_pub, msg) return True def subscribe(self): pub = self.__conn.pubsub() pub.subscribe(self.chan_sub) pub.parse_response() return pub
def Get_data(request): import os,re from itertools import islice # ip = request.POST('ip') ip=str(request.POST.getlist('hosts')) software=request.POST.getlist('software') os.system('chmod 600 wls/static/gitlabilanni.gitlabilanni') License = 'wls/static/gitlabilanni.gitlabilanni' InventoryFile='wls/static/hosts' if ip != [] and software != []: installfile = 'wls/static/yaml/%s' % (software[0]) a=ansinstall.MyRunner(InventoryFile,ip,installfile,License) result=a.Install() wo=a.get_result() else: wo='' return HttpResponse(json.dumps({ 'result':wo })) def ansible(request): import os,re from itertools import islice InventoryFile='wls/static/hosts' f=open(InventoryFile) hosts=[] for line in f.readlines(): if re.match(r'\[.*\]',line): pass else: hosts.append(line) f.close() softwares = [] for root, dirs, files in os.walk('wls/static/yaml/'): for i in files: if re.match(r'.*\.retry',i): pass else: softwares.append(i) return render(request, 'form/ansible.html', { 'softwares':softwares, 'hosts':hosts }) from . import funredis #这个是我自己写的一个模块 上面提过 obj = funredis.RedisHelper() #以下是redis的订阅功能,不懂可以看下文档 redis_sub = obj.subscribe() @require_websocket #该装饰符功能:是下面连接只接受websocket 不接收http连接 def echo_once(request): #接收端 while True: #循环取出数据到websocket 因为每次调用该接口都会执行while循环,并且不会结束,所以我做了判断在什么情况下结束该循环(PS:这里我想了很多方法都行不通,想了想就这个比较合适,所以我这里先用这个) msg = redis_sub.parse_response() if msg[2] == b'error' or msg[2] == b'pass' or msg[2] == b'end': request.websocket.send(msg[2]) print('stop') break else: request.websocket.send(msg[2]) 生产者:
from . import funredis def pro(res_obj): STD = ['stdout', 'stderr', 'msg'] res=res_obj._result 获取ansible的结果 info = "" if type(res) == type(dict()): for field in STD: if field in res.keys() and len(res[field]) > 0: info = u'{0}:{1}'.format(field, res[field]) log_info = u'{0}: {1}\n{2}'.format(res_obj._host, res_obj._task, info) #获取host名 task名 以及info信息 errornum='' if str(res_obj._task)=='TASK: end': log_info = u'end' if res['rc'] !=0: errornum=u'error' if res['rc'] =='': errornum=u'pass' obj = funredis.RedisHelper() if errornum !='': obj.public(errornum) else: obj.public(log_info)
下面是可选项 我用来生成日志文件的地方:
loader = DataLoader() FIELDS = ['cmd', 'command', 'start', 'end', 'delta', 'msg', 'stdout', 'stderr'] STD = ['stdout', 'stderr', 'msg'] logger = logging.getLogger('ansible') fh = logging.FileHandler('/var/log/ansible.log') logger.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) def ansible_log(res_obj): res=res_obj._result info = "" if type(res) == type(dict()): for field in STD: if field in res.keys() and len(res[field]) > 0: info = u'{0}:{1}'.format(field, res[field]) log_info = u'{0}: {1} {2}'.format(res_obj._host, res_obj._task, info) print(log_info) logger.info(log_info) 日志文件大概是这个样子的: