Python-Jenkins助力传统发布流程
首先看页面
在按下提交按钮后后端开始执行发布程序(jenkins),执行完成之后(成功/失败)返回如下结果
在Console Output 页面可以详细看到Jenkins执行过程(Python-Jenkins的功能)
每次操作记录都会写入到MySQL
应用和IP对应关系
引子
Jenkins虽然很方便很自动化了,但是总会有定制化的场景和需求,本例在Jenkins自动构建的基础之上再进一步的减少操作流程,让日常测试人员的升级、版本管理更加高效,将项目迭代的流程打包成“一键发布”。
本例的前提是Jenkins自动化构建已经存在,CMDB已建立完善
研发流程简介
略...
软件版本简介
- OS RHEL6.5
- Python 2.6.6
- Django 1.6.8
- Jenkins 2.131
- Gitlab gitlab-ce-7.14.0-ce.0.el6.x86_64.rpm
- JAVA 1.8.0_91
公司网络环境复杂,对稳定性和安全性较高,所以软件版本比较老旧
Django目录结构简介
[root@BETA-CMDB-24 opt]# tree /opt/yjbops/ /opt/yjbops/ ├── amail ├── cmdb ├── django_wsgi.py ├── log │ ├── amail_uwsgi.log │ ├── yjbops_access_log │ ├── yjbops_error_log │ ├── yjbops__uwsgi.log │ └── yjbops_uwsgi.log ├── manage.py ├── minionconf ├── motjob ├── mtrade ├── ops -------本例主目录 │ ├── admin.py │ ├── __init__.py │ ├── models.py ------- 模型(mysql) │ ├── models.pyc │ ├── urls.py ------- 路由(url) │ ├── views.pyc ------- 视图 │ └── views.pyc ├── pubs │ ├── admin.py │ ├── commonlist.py │ ├── common.py │ ├── common.pyc │ ├── html_helper_alist.py │ ├── html_helper_list.py │ ├── html_helper.py │ ├── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ ├── views.py ├── stop.sh ├── templates │ ├── amail │ ├── cmdb │ ├── master │ ├── minionconf │ ├── motjob │ ├── mtrade │ ├── ops ------- 本例页面主目录 │ │ ├── app_list.html │ │ ├── from.html │ │ ├── index.html │ │ ├── ips_list.html │ │ ├── release.html -------- 发布页面 │ │ └── release_record.html -------- 历史记录展示页面 │ ├── pubs │ └── registration │ ├── login.html │ └── profile.html ├── upload │ └── 20160927.xlsx ├── uwsgi.xml ├── wsgi.py └── yjbops ├── __init__.py ├── settings.py ├── urls.py ├── wsgi.py
Django路由
url(r'^index/', views.index), url(r'^release/', views.release), url(r'^release_record/', views.release_record), url(r'^app_list/', views.app_list), url(r'^ips_list/', views.ips_list),
这里只描述项目下面的路由配置
发布页面
视图函数
def release(request): ret = { 'applist': None, 'envlist': None, 'app_name': None, 'jira_url':None, 'svn_url':None, 'git_url':None, 'svn_num':None, 'git_id':None, 'time':time.strftime('%Y-%m-%d %H:%M:%S'), 'ips': None, 'console_status': None, 'currnet_buildnum': None, 'console_output': None } jenkins_server_url = 'http://192.168.24.191:8080/jenkins/' user_id = 'testuser' api_token = '7ca1a78d7eb1149137d432c4cf3aaeb3' server = jenkins.Jenkins(jenkins_server_url, username=user_id, password=api_token) job_name = 'FZ.TradeDeployPythonMySQL' ### Py-MySQL conn = MySQLdb.connect(host='127.0.0.1', user='root', passwd='123321', db='ops01') cur = conn.cursor() ### 定义触发jenkins所需参数 iplist = [] app_name = '' app_del = '' svn_url = '' svn_num = '' jira_url = '' IPs = '' stages_name = '' ### 下拉框 applist = models.Cmdb_app.objects.raw('select * from cmdb_app order by id; ') envlist = models.Cmdb_env.objects.raw('select * from cmdb_env order by id; ') ### 获取用户输入 if request.method == 'POST': app_name = request.POST.get('appname', None) jira_url = request.POST.get('jira', None) svn_num = request.POST.get('svn', None) stages_name = request.POST.get('envname', None) is_empty = all([app_name, jira_url, svn_num, stages_name, ]) if is_empty: ret['status'] = '服务名:%s JIRA单:%s SVN版本:%s 发布环境:%s'.decode('utf-8') % (app_name, jira_url, svn_num, stages_name) ip_sql = 'select ip from cmdb_ip where app_id in (select id from cmdb_app where appname="%s") and env_id in (select id from cmdb_env where envname="%s");' % (app_name, stages_name) ipsCount = cur.execute(ip_sql) res = cur.fetchall() for i in res: iplist.append(i[0]) IPs = ' '.join(iplist) svnCount = cur.execute('select appsvn from cmdb_app where appname="%s"; ' % (app_name)) svn_url = cur.fetchall()[0][0] gitCount = cur.execute('select appgit from cmdb_app where appname="%s"; ' % (app_name)) git_url = cur.fetchall()[0][0] rmdirCount = cur.execute('select appdel from cmdb_app where appname="%s"; ' % (app_name)) app_del_src = cur.fetchall()[0][0] if '/' not in app_del_src: app_del = app_del_src ### 执行带参数的 jenkins job param_dict = {'app_name': app_name, 'app_del': app_del, 'svn_url': svn_url, 'svn_num': svn_num, 'jira_url': jira_url, 'IPs': IPs, 'stages_name': stages_name} server.build_job(job_name, parameters=param_dict) # 触发远程 jenkins job time.sleep(5) # 为了产生最新一次的 id 防止报错 currnet_buildnum = server.get_job_info(job_name)['lastBuild']['number'] print 'currnet_buildnum',currnet_buildnum while True: time.sleep(1) # 1 秒钟监测一次 currnet_buildnum_1 = server.get_job_info(job_name)['lastBuild']['number'] print 'currnet_buildnum_1',currnet_buildnum_1 build_status = server.get_build_info(job_name, currnet_buildnum)['building'] print build_status # False if not build_status: # job 执行结束 print '执行结束...' console_output = server.get_build_console_output(job_name, currnet_buildnum) # 构建详情 # 取到 git 号 和 执行状态 for i in console_output.split('\n'): b = i.encode('utf-8') print b,type(b) if b.startswith("项目") and 'git' in b: git_id = re.findall('\w{7}', b)[0] if 'Finished' in b and 'SUCCESS' in b: console_status = 'SUCCESS' if 'Finished' in b and 'FAILURE' in b: console_status = 'FAILURE' ret['time'] = time.strftime('%Y-%m-%d %H:%M:%S') ret['app_name'] = app_name ret['jira_url'] = jira_url ret['svn_url'] = svn_url ret['git_url'] = git_url ret['svn_num'] = svn_num ret['git_id'] = git_id ret['ips'] = IPs ret['console_status'] = console_status ret['currnet_buildnum'] = str(currnet_buildnum) # 最新的构建号 ret['console_output'] = console_output # 录入本次升级记录 models.Cmdb_record.objects.create(appname = app_name, build_id = currnet_buildnum, ips = IPs, svn_num = svn_num, git_num = git_id, jira_num = jira_url, console_status = console_status, time = time.strftime('%Y%m%d%H%M%S'), ) break else: print 'building...' continue else: ret['status'] = '程序目录填写有误,请核对mysql数据并重新发布' exit() else: ret['status'] = '请填写所有选项' cur.close() conn.close() ret['applist'] = applist # 服务名下拉框 ret['envlist'] = envlist # 发布环境下拉框 return render_to_response('ops/release.html', ret)
多表查询使用了原生的SQL没有用Django的ORM框架
模板
<label> 服务名 </label> <select name="appname"> {% for item in applist %} <option value="{{ item.appname }}"> {{ item.appname }} </option> {% endfor %} </select> ... <dl id="dt-list-1"> <dd><b>AppName :</b> {{ app_name }}</dd> <dd><b>JIRA_URL :</b> <a href={{ jira_url }}>{{ jira_url }}</a></dd> <dd><b>SVN_URL :</b> <a href={{ svn_url }}>{{ svn_url }} </a></dd> <dd><b>GIT_URL :</b> {{ git_url }}</dd> <dd><b>SVN_NUM :</b> {{svn_num}}</dd> <dd><b>GIT_NUM :</b> {{git_id}}</dd> <dd><b>EXEC_IPs :</b> {{ips}}</dd> <dd><b>STATUS :</b> {{console_status}}</dd> <dd><b>Comments :</b> {{time}} 交易组-自动化部署</dd> </dl>
python-jenkins用法介绍
官方文档 :jenkins 官方wiki(触发job方式): https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API
官方文档 :Python-Jenkins官网:https://pypi.python.org/pypi/python-jenkins/
官方文档 :Python-Jenkins Doc:http://python-jenkins.readthedocs.io/en/latest/index.html
官方文档 :Python-Jenkins API:http://python-jenkins.readthedocs.io/en/latest/api.html