Jenkins Python API and Multi-Configuration Projects

2.7k views Asked by At

I have set up a multi-configuration project in Jenkins and I'm trying to manage those jobs through a command line script. I'm trying to use the jenkinsapi Python module for this. So far I've found how to get my job, but I can't figure out how to get specific build configurations to see if they've succeeded.

from jenkinsapi.jenkins import *
jenkins = Jenkins('http://example.org/jenkins')
job = jenkins.get_job('foobar')

# I can get job info here, but I don't know how to access specific configurations

Is there anyway to do this? Is each one of the multi-configuration projects a separate "job" or are they all lumped into the same job? I'm going to have to manage a lot of these types of projects in the future, and I want to be able to add a new hardware configuration to the build easily.

3

There are 3 answers

2
malenkiy_scot On

I do not think Python JenkinsAPI is suitable for working with Matrix jobs. Use Groovy scripts via Groovy Plugin to tap into Jenkins Java API.

Matrix classes live in hudson.matrix module.

0
Larry Cai On

Not so clear what you want, give some hints.

If you are not clear what information you can from object job in the API, you can use below method to check

Check the source codes in github, it is job.py for your case, also you can use python dir() method to see which you can call in job

>>> from jenkinsapi.jenkins import *
>>> jenkins = Jenkins('http://localhost')
>>> job = jenkins.get_job('foobar')
>>> dir(job)
['RETRY_ATTEMPTS', '__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__get
item__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__s
izeof__', '__str__', '__subclasshook__', '__weakref__', '_buildid_for_type', '_config', '_data', '_element_tree', '_get_
config_element_tree', '_mk_json_from_build_parameters', '_poll', '_revmap', '_scm_map', '_scmbranchmap', '_scmurlmap', '
baseurl', 'delete_from_queue', 'disable', 'enable', 'get_build', 'get_build_dict', 'get_build_ids', 'get_build_triggerur
l', 'get_buildnumber_for_revision', 'get_config', 'get_config_xml_url', 'get_data', 'get_delete_url', 'get_description',
 'get_downstream_job_names', 'get_downstream_jobs', 'get_first_build', 'get_first_buildnumber', 'get_jenkins_obj', 'get_
last_build', 'get_last_build_or_none', 'get_last_buildnumber', 'get_last_completed_build', 'get_last_completed_buildnumb
er', 'get_last_failed_buildnumber', 'get_last_good_build', 'get_last_good_buildnumber', 'get_next_build_number', 'get_pa
rams', 'get_params_list', 'get_queue_item', 'get_rename_url', 'get_revision_dict', 'get_scm_branch', 'get_scm_type', 'ge
t_scm_url', 'get_upstream_job_names', 'get_upstream_jobs', 'invoke', 'is_enabled', 'is_queued', 'is_queued_or_running',
'is_running', 'jenkins', 'load_config', 'mk_json_from_build_parameters', 'modify_scm_branch', 'modify_scm_url', 'name',
'poll', 'python_api_url', 'strip_trailing_slash', 'update_config']
>>> print job.get_last_build()
foobar #5

Check the configuration directly, click REST API in the bottom of your foobar job, you can get the job configuration directly like http://example.org/jenkins/job/foobar/api/json?pretty=true

0
Jan On

The easiest way I found to dealing with multi / matrix configurations is by loading it as a job. The .../api/python data for a configuration is exactly the same as a normal job. So we can just access it like one with a few changes to the api.

Edit the: process_job_folder function in jenkinsbase.py

and get_full_name_from_url_and_baseurl in job.py

Either edit the api code or just overwrite the functions. (bit ugly but works ^_^)

I also made a small change to how the names are displayed. This way the naming is more consistent with: jobs, "sub"? jobs and configurations

for example:

Job Name/Job Name/Configuration

import urlparse
from jenkinsapi import job
from jenkinsapi import jenkins

def process_job_folder(self, folder, folder_path):
    folder_path += '/job/%s' % folder['name']
    data = self.get_data(self.python_api_url(folder_path),
                         tree='jobs[name,color]')
    result = []
    for job in data.get('jobs', []):
        if 'color' not in job.keys():
            result += self.process_job_folder(job, folder_path)
        else:
            job['url'] = '%s/job/%s' % (folder_path, job['name'])
            ## Added to prevent name conflicts
            job['name'] = folder['name'] + "/" + job['name']
            result.append(job)
            ## Added configuration support
            configData = self.get_data(self.python_api_url(job['url']),
                                       tree='activeConfigurations[name,color,url]')
            for config in configData.get("activeConfigurations", []):
                config['url'] = '%s/%s' % (job['url'], config["name"])
                config["name"] = job['name'] + "/" + config["name"]
                result.append(config)
            ## End of edit
    return result

@staticmethod
def get_full_name_from_url_and_baseurl(url, baseurl):
    """
    Get the full name for a job (including parent folders) from the
    job URL.
    """
    path = url.replace(baseurl, '')
    split = path.split('/')
    ## Ignore /job and empty strings
    split = [urlparse.unquote(part) for part in split[::] if not part in ["", "job"]]
    ## End of edit
    return '/'.join(split)

jenkins.JenkinsBase.process_job_folder = process_job_folder
job.Job.get_full_name_from_url_and_baseurl = get_full_name_from_url_and_baseurl

def get_server_instance():
    jenkins_url = SERVER_URL
    server = jenkins.Jenkins(jenkins_url,
                     username = USERNAME, password = PASSWORD_OR_API_KEY,
                     ssl_verify=False,
                     timeout=50)
    return server

s = get_server_instance()
for j in s.get_jobs():
    print j

*Edit:

Now you can do:

s = get_server_instance()
myJobName = "foobar/platform=x64"
j = s.get_job(myJobName)

latestBuild = j.get_last_build()
print "Latest build number: ",latestBuild.get_number()
print "\tLatest build status: ",latestBuild.get_status()