摘要: jenkins(持续集成开源工具)提供了丰富的api接口,基本上所有的操作都可以使用curl来从后台调度,包括:创建项目,禁用项目,启用项目,获取项目描述,获取配置文件,普通触发,scm触发,带参数触发,带补丁触发。同时也可以使用python 库 jenkinsapi https://pypi.python.org/pypi/jenkinsapi

【背景】:部门在搞持续集成,使用jenkins作为核心调度,要再其基础上进行二次封装,所以需要研究下jenkins的api。笔者主要负责搭建平台,在研究用法也花费了些时间,本文主要做个简要的记录,希望能为各位朋友节省时间。

【环境】:(默认读者已经具备了基本的持续集成经验和jenkins用法)

1.Jenkins1.455

2. 系统Suse

3. Tomcat 6.0.37

4. Java 1.6.0_26

5. patch-parameter

【API介绍】

Jenkins提供了html、json、python API,实质都是以http get/post方式调用的。查看http://www.xxx.xxx/jenkins/api/ 即可得到相应的说明,如图:

【API实践】

1.创建

curl -X POST http://www.xxx.xxx/jenkins/createItem?name=JavaStd  --user peterguo:peterguo --data-binary "@javastd.config.xml" -H "Content-Type: text/xml"

2.禁用

curl -X POST http://www.xxx.xxx/jenkins/job/JavaStd/disable  --user peterguo:peterguo

3.启用

curl -X POST http://www.xxx.xxx/jenkins/job/JavaStd/enable --user peterguo:peterguo

4.删除

curl -X POST http://www.xxx.xxx/jenkins/job/JavaStd/doDelete --user peterguo:peterguo

5.获取项目描述

curl -X GET http://www.xxx.xxx/jenkins/job/JavaStd/description --user peterguo:peterguo

6.获取配置文件

curl -X GET http://www.xxx.xxx/jenkins/job/JavaStd/config.xml --user peterguo:peterguo

7.触发SCM检查

curl -X GET http://www.xxx.xxx/jenkins/job/JavaStd/polling --user peterguo:peterguo

8.普通触发

curl -X GET http://www.xxx.xxx/jenkins/job/JavaStd/build --user peterguo:peterguo

9.带参数触发

curl -X GET "http://www.xxx.xxx/jenkins/job/helloworld-freestyle/buildWithParameters?bAllTest=&Choices=2&strParam=abc" --user peterguo:peterguo

10.带文件触发

curl  -X POST "http://localhost:8080/job/Demo/buildWithParameters?assertMethod=online" -F "input=@atest.txt"

11.参数和补丁触发

curl -X POST "http://www.xxx.xxx/jenkins/job/helloworld-freestyle/buildWithParameters?bAllTest=&Choices=2&strParam=abc" --user peterguo:peterguo -F "action=upload" -F "patch.diff=@OtherTest.java.patch"

注:带补丁触发需要先安装补丁插件,并设置项目的补丁参数

【通过Python-curl库调用】

提供python使用pycurl调用的例子片段,实际和curl调用一样,优点是易整合。

import pycurl
url = "http://10.129.145.112:8081/jenkins/job/helloworld-freestyle/config.xml"
crl = pycurl.Curl()
crl.setopt(pycurl.VERBOSE,1)
crl.setopt(pycurl.FOLLOWLOCATION, 1)
crl.setopt(pycurl.MAXREDIRS, 5)
crl.setopt(pycurl.USERPWD, "peterguo:peterguo") crl.setopt(pycurl.CONNECTTIMEOUT, 60)
crl.setopt(pycurl.TIMEOUT, 300)
crl.setopt(pycurl.HTTPPROXYTUNNEL,1)
crl.fp = StringIO.StringIO() crl.setopt(pycurl.URL, url)
crl.setopt(crl.WRITEFUNCTION, crl.fp.write)
crl.perform()
ret = crl.fp.getvalue()
 

【通过Python-jenkinsapi库调用】

这里给出代码

from jenkinsapi.jenkins import Jenkins

class CJenkinsAPI():
'''
均采用同步设置超时机制
创建项目:输入:configid planid
创建任务:输入:configid planid 返回:返回码 msg buildid 额外动作:不写SQL
查询任务:输入:configid planid taskid 返回:返回码 msg buildid 额外动作:结束更新SQL(包括成功或失败),未结束则不处理
终止任务:输入:configid planid taskid 返回:返回码 msg buildid 额外动作:终止成功写SQL '''
__doc__ = '''Usage: \t\tCJenkinsAPI.createProject\t\tCJenkinsAPI.triggerBuild\t\t''' _strConfigTemplatePath = ""
_strConfigDataPath = "" def __init__(self):
import pycurl
pass def __del__(self):
pass @staticmethod
def createProject(nPlanId, strConfigId):
'''
Return:返回码/返回信息
先根据配置文件生成项目
'''
#用于测试
nPlanId = 14
strConfigId = "D1057406" #返回
nRet, strMsg, nBuild = 0, "", 0 #配置文件模版
strConfigTemplate = CJenkinsAPI._strConfigTemplatePath + "/config.template.xml" #用planID和配置ID作为项目名
strProjectName = "P%d-%s" % (nPlanId, strConfigId ) #访问数据库拿到构建节点IP和SVN
strBuildNodeIP = ""
strProjectSVN = "" oProxy = CSqlProxy("10.129.145.112", "ci_test", "ci_test", "ci_plat")
#SVN从t_build_plan中取
lSelectRet = CSqlProxy.selectFromTable(oProxy.m_oCon, "t_build_plan", ["f_svn"], " where f_plan_id='%d' " % nPlanId)
strProjectSVN = lSelectRet[0]["f_svn"]
#配置信息从t_ci_config中取
lSelectRet = CSqlProxy.selectFromTable(oProxy.m_oCon, "t_ci_config", ["f_node_ip", "f_run_param"], " where f_config_id='%s' " % strConfigId)
strBuildNodeIP = lSelectRet[0]["f_node_ip"]
strRunParam = lSelectRet[0]["f_run_param"]
oProxy.close() strNodeFlag = {True:"", False:"slave_"}["10.129.145.112" in strBuildNodeIP]
dReplaceInfo = {"PROJ_DESC" : "This is generate by Ci-plat, with plan_id[%d] and config_id[%s]" % (nPlanId, strConfigId),\
"SVN_URL" : strProjectSVN.replace("http://tc-svn.tencent.com", "svn://172.27.198.49:8081"),\
"BUILD_NODE_IP" : strNodeFlag + strBuildNodeIP,\
"BUILD_SCRIPT" : '''sh build.sh %s ''' % strRunParam, \
"JUNIT_TEST_PATH" :"target/surefire-reports/*.xml", \
"COVERAGE_PATH" :"target/site/cobertura/", \
} #利用模版生成配置文件
oConf = CCiConfig(strConfigTemplate, dReplaceInfo)
strCurConfigXml = CJenkinsAPI._strConfigDataPath + "/" + strProjectName + ".config.xml"
oConf.dump2file(strCurConfigXml) strCommand = 'curl -X POST http://10.129.145.112:8081/jenkins/createItem?name=%s --user peterguo:peterguo --data-binary "@%s" -H "Content-Type: text/xml"' % (strProjectName, strCurConfigXml)
nRet = os.system(strCommand)
strMsg = {True:"SucceedCreate,Url:", False:"FailedCreate,Url:"}[nRet==0]
print "%d|%s|%d" % (nRet, tran2UTF8(strMsg)+"[http://10.129.145.112:8081/jenkins/job/%s]" % strProjectName, nBuild) @staticmethod
def triggerBuild(nPlanId, strConfigId):
'''
Return:
触发前先更新配置文件,使用远程脚本
触发前获取要出发的编号
'''
#返回
nRet, strMsg, nBuild = 0, "", 0 #配置文件模版
strConfigTemplate = CJenkinsAPI._strConfigTemplatePath + "/config.template.xml" #用planID和配置ID作为项目名
strProjectName = "P%d-%s" % (nPlanId, strConfigId ) #访问数据库拿到构建节点IP和SVN
strBuildNodeIP = ""
strProjectSVN = "" oProxy = CSqlProxy("10.129.145.112", "ci_test", "ci_test", "ci_plat")
#SVN从t_build_plan中取
lSelectRet = CSqlProxy.selectFromTable(oProxy.m_oCon, "t_build_plan", ["f_svn"], " where f_plan_id='%d' " % nPlanId)
strProjectSVN = lSelectRet[0]["f_svn"]
#配置信息从t_ci_config中取
lSelectRet = CSqlProxy.selectFromTable(oProxy.m_oCon, "t_ci_config", ["f_node_ip", "f_run_param"], " where f_config_id='%s' " % strConfigId)
strBuildNodeIP = lSelectRet[0]["f_node_ip"]
strRunParam = lSelectRet[0]["f_run_param"]
oProxy.close() strNodeFlag = {True:"", False:"slave_"}["10.129.145.112" in strBuildNodeIP]
dReplaceInfo = {"PROJ_DESC" : "This is generate by Ci-plat, with plan_id[%d] and config_id[%s]" % (nPlanId, strConfigId),\
"SVN_URL" : strProjectSVN.replace("http://tc-svn.tencent.com", "svn://172.27.198.49:8081"),\
"BUILD_NODE_IP" : strNodeFlag + strBuildNodeIP,\
"BUILD_SCRIPT" : '''sh build.sh %s ''' % strRunParam, \
"JUNIT_TEST_PATH" :"target/surefire-reports/*.xml", \
"COVERAGE_PATH" :"target/site/cobertura/", \
} #利用模版生成配置文件
oConf = CCiConfig(strConfigTemplate, dReplaceInfo)
strCurConfigXml = CJenkinsAPI._strConfigDataPath + "/" + strProjectName + ".config.xml"
oConf.dump2file(strCurConfigXml) #更新配置文件
strCommand = 'curl -X POST http://10.129.145.112:8081/jenkins/job/%s/config.xml --user peterguo:peterguo --data-binary "@%s" -H "Content-Type: text/xml"' % (strProjectName, strCurConfigXml)
nRet = os.system(strCommand)
strMsg += {True:"更新配置成功", False:"更新配置失败"}[nRet==0] #获取下一次构建编号
nBuild = Jenkins("http://10.129.145.112:8081/jenkins","peterguo","peterguo")[strProjectName.encode("utf8")].get_next_build_number() #触发构建
strCommand = 'curl -X POST http://10.129.145.112:8081/jenkins/job/%s/build --user peterguo:peterguo ' % (strProjectName)
nRet = os.system(strCommand)
strMsg = {True:"SucceedTrigger,Url:", False:"FailedTrigger,Url:"}[nRet==0]
print "%d|%s|%d" % (nRet, tran2UTF8(strMsg)+"[http://10.129.145.112:8081/jenkins/job/%s/%d]" % (strProjectName, nBuild), nBuild ) @staticmethod
def infoBuild(nPlanId, strConfigId, nTaskId):
'''
Return:
'''
strProjectName = "P%d-%s" % (nPlanId, strConfigId )
oProxy = CSqlProxy("10.129.145.112", "ci_test", "ci_test", "ci_plat")
strWhere = " where f_task_id='%d' " % int(nTaskId)
lSelectRet = CSqlProxy.selectFromTable(oProxy.m_oCon, "t_build_task", ["f_build_id"], strWhere)
oProxy.close()
nBuildId = int(lSelectRet[0]["f_build_id"]) oCurBuild = Jenkins("http://10.129.145.112:8081/jenkins","peterguo","peterguo")[strProjectName.encode("utf8")].get_build(nBuildId)
bRunning = oCurBuild.is_running()
if bRunning == True:
print "1|Running|%d" % nBuildId
return #最重要更新的数据
dResult2Sql = {} #取测试用例结果的个数信息
if oCurBuild.has_resultset():
dResult = oCurBuild.get_actions()
else:
dResult = {"failCount":0, "totalCount":0, "skipCount":0} oDeltaDur = oCurBuild.get_duration()
oBuildBegin = utc2LocalDatetime(oCurBuild.get_timestamp())
oBuildEnd = oBuildBegin + oDeltaDur
dResult2Sql["f_case_fail"] = dResult['failCount']
dResult2Sql["f_case_total"] = dResult['totalCount']
dResult2Sql["f_case_skip"] = dResult['skipCount']
dResult2Sql["f_build_duration"] = "%.3f" % (oDeltaDur.days * 24 * 60 + oDeltaDur.seconds / 60.0)
dResult2Sql["f_build_url"] = oCurBuild.baseurl
dResult2Sql["f_build_result"] = {True:0, False:1}[oCurBuild.is_good()]
dResult2Sql["f_task_status"] = TASK_DONE
dResult2Sql["f_build_time"] = oBuildBegin.strftime("%Y-%m-%d %H:%M:%S")
dResult2Sql["f_build_end"] = oBuildEnd.strftime("%Y-%m-%d %H:%M:%S")
dResult2Sql["f_msg_info"] = tran2GBK("任务完成,收集数据完成") #任务已经完成,需要入库相关数据,更新相关状态
oProxy = CSqlProxy("10.129.145.112", "ci_test", "ci_test", "ci_plat")
strWhere = " where f_task_id='%d' " % int(nTaskId)
CSqlProxy.updateValueToDBTable(oProxy.m_oCon, "t_build_task", dResult2Sql.keys(), dResult2Sql.values(), strWhere)
oProxy.close()
#for item in dResult2Sql.items():
# print item[0], str(item[1])
print "%d|%s|%d" % (0, "SucceedUpdated", nBuildId) @staticmethod
def stopBuild(nPlanId, strConfigId, nTaskId):
'''
Return:
'''
strProjectName = "P%d-%s" % (nPlanId, strConfigId )
oProxy = CSqlProxy("10.129.145.112", "ci_test", "ci_test", "ci_plat")
strWhere = " where f_task_id='%d' " % int(nTaskId)
lSelectRet = CSqlProxy.selectFromTable(oProxy.m_oCon, "t_build_task", ["f_build_id"], strWhere)
oProxy.close()
nBuildId = int(lSelectRet[0]["f_build_id"]) oCurBuild = Jenkins("http://10.129.145.112:8081/jenkins","peterguo","peterguo")[strProjectName.encode("utf8")].get_build(nBuildId)
bRunning = oCurBuild.is_running()
if bRunning == False:
print "2|AlreadyStopped|%d" % nBuildId
return #触发停止命令
oCurBuild.stop() #等停止
oCurBuild.block_until_complete() #最重要更新的数据
dResult2Sql = {} #取测试用例结果的个数信息
if oCurBuild.has_resultset():
dResult = oCurBuild.get_actions()
else:
dResult = {"failCount":0, "totalCount":0, "skipCount":0} oDeltaDur = oCurBuild.get_duration()
oBuildBegin = utc2LocalDatetime(oCurBuild.get_timestamp())
oBuildEnd = oBuildBegin + oDeltaDur
dResult2Sql["f_case_fail"] = dResult['failCount']
dResult2Sql["f_case_total"] = dResult['totalCount']
dResult2Sql["f_case_skip"] = dResult['skipCount']
dResult2Sql["f_build_duration"] = "%.3f" % (oDeltaDur.days * 24 * 60 + oDeltaDur.seconds / 60.0)
dResult2Sql["f_build_url"] = oCurBuild.baseurl
dResult2Sql["f_build_result"] = {True:0, False:1}[oCurBuild.is_good()]
dResult2Sql["f_task_status"] = TASK_ABORTED
dResult2Sql["f_build_time"] = oBuildBegin.strftime("%Y-%m-%d %H:%M:%S")
dResult2Sql["f_build_end"] = oBuildEnd.strftime("%Y-%m-%d %H:%M:%S")
dResult2Sql["f_msg_info"] = tran2GBK("TaskStopped") #任务已经完成,需要入库相关数据,更新相关状态
oProxy = CSqlProxy("10.129.145.112", "ci_test", "ci_test", "ci_plat")
strWhere = " where f_task_id='%d' " % int(nTaskId)
CSqlProxy.updateValueToDBTable(oProxy.m_oCon, "t_build_task", dResult2Sql.keys(), dResult2Sql.values(), strWhere)
oProxy.close()
#for item in dResult2Sql.items():
# print item[0], str(item[1]) print "%d|%s|%d" % (1, tran2UTF8("SucceedStopped"), nBuildId) @staticmethod
def deleteProject(nPlanId, strConfigId):
'''
Return:返回码/返回信息 curl -X POST http://10.129.145.112:8081/jenkins/job/JavaStd/doDelete --user peterguo:peterguo
'''
strProjectName = "P%d-%s" % (nPlanId, strConfigId )
strCommand = 'curl -X POST http://10.129.145.112:8081/jenkins/job/%s/doDelete --user peterguo:peterguo' % (strProjectName)
CColorPrint.colorPrintStr("CMD:[%s]" % strCommand, "green") nRet = os.system(strCommand)
strMsg = {True:"SucceedDeleted", False:"FailedDeleted"}[nRet==0]
print nRet, tran2UTF8(strMsg), 0

jenkins使用记录转自https://my.oschina.net/sanpeterguo/blog/197931的更多相关文章

  1. selenium定位元素(本内容从https://my.oschina.net/flashsword/blog/147334处转载)

    注明:本内容从https://my.oschina.net/flashsword/blog/147334处转载. 在使用selenium webdriver进行元素定位时,通常使用findElemen ...

  2. https://my.oschina.net/huangyong/blog/161419

    https://my.oschina.net/huangyong/blog/161419

  3. https://my.oschina.net/reesechou/blog/492265

    https://my.oschina.net/reesechou/blog/492265

  4. Echarts 的 Java 封装类库 转自 https://my.oschina.net/flags/blog/316920

    转自: https://my.oschina.net/flags/blog/316920 Echarts 的 Java 封装类库:http://www.oschina.net/p/echarts-ja ...

  5. linux svn 中文 https://my.oschina.net/VASKS/blog/659236

    https://my.oschina.net/VASKS/blog/659236 设置服务器: export LC_ALL=zh_CN.UTF-8长久之计, echo export LC_ALL=zh ...

  6. python和scrapy的安装【转:https://my.oschina.net/xtfjt1988/blog/364577】

    抓取网站的代码实现很多,如果考虑到抓取下载大量内容scrapy框架无疑是一个很好的工具.Scrapy = Search+Pyton.下面简单列出安装过程.PS:一定要按照Python的版本下载,要不然 ...

  7. c3p0的几种使用方式(原文地址: https://my.oschina.net/liangtee/blog/101047)

    package com.c3p0.test; import java.sql.Connection; import java.sql.SQLException; import java.beans.P ...

  8. python学习笔记之读取配置文件【转自https://my.oschina.net/u/3041656/blog/793467】

    [转自https://my.oschina.net/u/3041656/blog/793467] 最近在接触利用python来写测试框架,本人也是个刚接触python,所以是个小菜鸟,今天开始,一点点 ...

  9. https://www.oschina.net/project/lang/19/java

    https://www.oschina.net/project/lang/19/java

随机推荐

  1. NOIP模拟赛(by hzwer) T2 小奇的序列

    [题目背景] 小奇总是在数学课上思考奇怪的问题. [问题描述] 给定一个长度为 n 的数列,以及 m 次询问,每次给出三个数 l,r 和 P, 询问 (a[l'] + a[l'+1] + ... + ...

  2. Mac下Sublime text3无法安装Package Control及中文乱码问题

    sublime text3是一款轻量级的代码编辑器,我曾在Windows下配置过,但时间久了就忘了.这次是在mac上配置,在网上查了一些帖子,有的叙述不是很清楚,故记录一下详细过程. 在线安装: ht ...

  3. Rosetta Stone 不在C盘安装步骤

    本文出自:http://www.cnblogs.com/2186009311CFF/p/7500637.html Rosetta Stone默认安装在C盘的,很不好,故找到次解决方案: 总体就是移动文 ...

  4. Spring Boot文件上传

    一.创建一个简单的包含WEB依赖的SpringBoot项目 二.配置文件上传的文件大小限制 # 上传文件总的最大值spring.servlet.multipart.max-request-size=1 ...

  5. 14 Spring Boot Shiro限制登录尝试次数

  6. nginx 缓存设置

    浏览器缓存原理 浏览器缓存 HTTP协议定义的缓存机制(如:Expires:Cache-control等) 2.浏览器无缓存 3.客户端有缓存 校验过期机制 校验是否过期                ...

  7. Twice Equation

    题目链接:https://nanti.jisuanke.com/t/A1541 题意:给你一个L,要你求一个不小于L的最小数字n,对于一个整数m,满足2*(m+1)*m=n*(n+1). 思路:打表找 ...

  8. [CSP-S模拟测试]:序列(构造)

    题目描述 给定$N,A,B$,构造一个长度为$N$的排列,使得:$\bullet$排列长度为$N$:$\bullet$最长上升子序列长度为$A$:$\bullet$最长下降子序列长度为$B$.我们有$ ...

  9. Spring_Boot 简单例子

    第一步创建项目: 创建项目地址:https://start.spring.io/ 接下来就下载到本地了 跟着加压 接着用idea打开:等待资源下载完成 我写了个简单的:增删改查 项目结构: dao层: ...

  10. DAY 6 上午

    如果不是割点,答案减少2(n-1) 如果删去割点,删去之后整个图分成多个连通块 每一个联通块的大小*其他连通块的大小之和 先求出缩点之后的树 加尽可能少的边使树变成一个边双 找出树上的所有叶子节点(度 ...