Python-Jenkins助力传统发布流程

首先看页面

在按下提交按钮后后端开始执行发布程序(jenkins),执行完成之后(成功/失败)返回如下结果

在Console Output 页面可以详细看到Jenkins执行过程(Python-Jenkins的功能)

每次操作记录都会写入到MySQL

 应用和IP对应关系

引子

  Jenkins虽然很方便很自动化了,但是总会有定制化的场景和需求,本例在Jenkins自动构建的基础之上再进一步的减少操作流程,让日常测试人员的升级、版本管理更加高效,将项目迭代的流程打包成“一键发布”。

  本例的前提是Jenkins自动化构建已经存在,CMDB已建立完善

研发流程简介

略...

软件版本简介

公司网络环境复杂,对稳定性和安全性较高,所以软件版本比较老旧

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

 

 

 

 

 

    

 

posted @ 2018-07-13 14:59  RunnerMark  阅读(1437)  评论(0编辑  收藏  举报