其中,Lockable Resource这个技术点已经研究过了(见以前写的3篇文档):
本文主要描述如何使用Python代码启动一组Jenkins Job,并监控这些Job的运行状态。
Jenkins中的Job
对象(文档:https://javadoc.jenkins.io/hudson/model/Job.html),是在Jenkins中定义的任务,可以由Jenkins服务器执行和管理。Job
的配置信息都保存在一个XML文件中,定义了一系列的构建步骤和配置选项。
Job的REST API是:http://
Jenkins中的Build
对象(文档:https://javadoc.jenkins.io/hudson/model/Build.html),是Job
的一个具体实例,代表了一次构建的过程和结果。每次触发构建,都会生成一个新的Build
实例。可以通过Build
获取构建的状态、结果和日志信息。
Jenkins中的Queue
对象(文档:https://javadoc.jenkins-ci.org/hudson/model/Queue.html),任务队列,用于管理待执行的Job
。当触发构建时,Job
会被添加到Queue
中等待执行,生成QueueItem
对象。
Queue的REST API是:http://
在Jenkins中,QueueItem
是一个接口,用于表示Jenkins队列中的对象。当启动一个Build
时,Jenkins会首先在队列中创建一个QueueItem
,其中包含了Job
的配置信息、启动Parameters
以及发起Job的Actions
。当QueueItem
被分配给一个executor
执行时,它会与一个Build
对象关联。可以通过执行cancel
操作来取消QueueItem
,一旦QueueItem
被取消或者关联的Build
开始执行后几分钟后它会被删除。据说StackOverflow上某个非官方的帖子说是5分钟:“once a build job has started, the queue item has 5 minutes before it is deleted”。
Queue的REST API是:http://
常用于Jenkins管理的Python库包括:
在封装对象时,jenkinsapi对Restful API返回的数据进行了筛选,舍弃了一些不常用的信息。然而,对于QueueItem的封装无法体现QueueItem ID和对应的Build ID之间的对应关系。python-jenkins库的返回值就是API原始的返回值,包含了所有的信息,因此选择python-jenkins库。
JenkinsJob类的实现:
import jenkins
class JenkinsJob:
def __init__(self, job_url: str):
self.queue_id = None
self.queue_item = None
self.executable = None
self.params = None
self.status = None
# TODO:省略
# self.server = “获取根据地址缓存的jenkins.Jenkins实例”
# ...
def build_job(self, params: dict):
self.queue_id = self.server.build_job(self.job_name, params)
self.params = params
logging.info(f'{self} entered the queue')
def monitor_status(self) -> bool:
# The job has not been started: ignore to monitor
if self.queue_id is None:
self.status = 'NOT_BUILD'
return True
# The job is finished with status: ignore to monitor
if self.status:
return True
# The job is in queue: check if it is cancelled
if not self.executable:
self.queue_item = self.server.get_queue_item(self.queue_id)
if self.queue_item.get('cancelled'):
logging.info(f'{self} has been cancelled.')
self.status = 'QUEUE_CANCELLED'
return True
# The job is in queue: check if it is started
if self.executable is None and 'executable' in self.queue_item:
self.executable = self.queue_item['executable']
logging.info(f'{self} left queue, building started...')
# The job is started: check if it is finished with status
if self.executable:
self.build = self.server.get_build_info(self.job_name, self.executable['number'])
if status := self.build.get('result'):
logging.info(f'{self} set build status: {status}')
self.status = status
return True
return False
@classmethod
def wait_for_all(cls, jobs: List['JenkinsJob'], delay: int = 10):
while any(job.status is None for job in jobs):
time.sleep(delay)
for job in jobs:
job.monitor_status()
logger.debug('all jobs done')
调用代码:
def start_jenkins_job(job_url, test_params, timestamp):
jenkins_job = JenkinsJob(job_url)
params = {
'OS_NAME': test_params.os.upper(),
'MIXING': test_params.params['mixing'],
'TIMESTAMP': timestamp,
'PARAMS': str(test_params.params),
}
jenkins_job.build_job(params)
return jenkins_job
def run_all_jenkins_suite():
running_items = []
suite_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
for params in generate_parameters():
item = start_jenkins_job(job_urls[params.item], params, suite_timestamp)
if item is not None:
running_items.append(item)
JenkinsJob.wait_for_all(running_items)
return running_items
run_all_jenkins_suite()
函数调用start_jenkins_job()
函数,启动一个个的构建并将其添加到running_items
中。然后调用JenkinsJob.wait_for_all()
函数,等待所有的构建执行完毕。
JenkinsJob.wait_for_all()
函数每隔10
秒(使用delay
参数)检查所有Job的执行状态(通过monitor_status()
函数)。如果所有的Job的执行状态都不为None
,则退出等待。
monitor_status()
函数的执行逻辑如下:
queue_id
:如果Job没有queue_id
,则表示尚未进行构建,将status
设置为NOT_BUILD
,然后停止状态检查。status
字段,如果不为None
,表示构建已经完成,停止状态检查。executable
字段,这个字段只有在QueueItem
获取到executor
后才会被设置。executable
字段时,保存其中构建的url
和number
信息。url
和number
信息后,可以调用RESTful API检查构建的状态。result
字段,表示构建已结束,将其值用于更新status
字段。参考文档:
- https://stackoverflow.com/questions/45472604/get-jenkins-job-build-id-from-queue-id
- https://python-jenkins.readthedocs.io/
- https://jenkinsapi.readthedocs.io/en/latest/index.html