Getting Jenkins Jobs by Build State with Python

I have been working with Python and Jenkins a lot lately and recently needed to find a way to check the job’s status at the build level. I discovered the jenkinsapi package and played around with it to see if it would give me the ability to drill down to the build and resultset level within Jenkins.

In the builds that I run, there are X number of sub-jobs. Each of these sub-jobs can pass or fail. If one of them fails, the entire build is marked with the color yellow and tagged as “UNSTABLE”, which is failed in my book. I want a way to track which of these sub-jobs is failing and how often over a time period. Some of these jobs can be unstable because they access network resources, which others may have been broken by a recent commit to the code base.

I eventually came up with some code that helps me figure out some of this information. But before you can dive into the code, you will need to install a package.


Installing the Prerequisites

The jenkinsapi package is easy to install because it is pip-compatible. You can install it to your main Python installation or in a Python virtual environment by using the following command:

pip install jenkinsapi

You will also need to install requests, which is also pip-compatibe:

pip install requests

These are the only packages you need. Now you can move on to the next section!

Querying Jenkins

The first step that you will want to accomplish is getting the jobs by their status. The standard statuses that you will find in Jenkins are SUCCESS, UNSTABLE or ABORTED.

Let’s write some code to find only the UNSTABLE jobs:

from jenkinsapi.jenkins import Jenkins
from jenkinsapi.custom_exceptions import NoBuildData
from requests import ConnectionError

def get_job_by_build_state(url, view_name, state='SUCCESS'):
    server = Jenkins(url)
    view_url = f'{url}/view/{view_name}/'
    view = server.get_view_by_url(view_url)
    jobs = view.get_job_dict()

    jobs_by_state = []

    for job in jobs:
        job_url = f'{url}/{job}'
        j = server.get_job(job)
        try:
            build = j.get_last_completed_build()
            status = build.get_status()
            if status == state:
                jobs_by_state.append(job)
        except NoBuildData:
            continue
        except ConnectionError:
            pass

    return jobs_by_state

if __name__ == '__main__':
    jobs = get_job_by_build_state(url='http://myJenkins:8080', view_name='VIEW_NAME',
                                  state='UNSTABLE')

Here you create an instance of Jenkins and assign it to server. Then you use get_view_by_url() to get the specified view name. The view is basically a set of associated jobs that you have set up. For example, you might create a group of jobs that does dev/ops type things and put them into a Utils view, for example.

Once you have the view object, you can use get_job_dict() to get you a dictionary of all the jobs in that view. Now that you have the dictionary, you can loop over them and get the individual jobs inside the view. You can get the job by calling the Jenkin’s object’s get_job() method. Now that you have the job object, you can finally drill down to the build itself.

To prevent errors, I found that you could use get_last_completed_build() to get the last completely build. This is for the best as if you use get_build() and the build hasn’t finished, the build object may not have the contents that you expect in it. Now that you have the build, you can use get_status() to get its status and compare it to the one that you passed in. If they match, then you add that job to jobs_by_state, which is a Python list.

You also catch a couple of errors that can happen. You probably won’t see NoBuildData unless the job was aborted or something really odd happens on your server. The ConnectionError exception happens when you try to connect to a URL that doesn’t exist or is offline.

At this point you should now have a list of the jobs filtered to the status that you asked for.

If you’d like to drill down further into sub-jobs within the job, then you need to call the build’s has_resultset() method to verify that there are results to inspect. Then you can do something like this:

resultset = build.get_resultset()
for item in resultset.items():
    # do something here

The resultset that is returned varies quite a bit depending on the job type, so you will need to parse the item tuple yourself to see if it contains the information you need.


Wrapping Up

At this point, you should have enough information to start digging around in Jenkin’s internals to get the information you need. I have used a variation of this script to help me extract information on builds that have failed to help me discover jobs that have failed repeatedly sooner than I would have otherwise. The documentation for jenkinsapi is unfortunately not very detailed, so you will be spending a lot of time in the debugger trying to figure out how it works. However it works pretty well overall once you figure it out.

Print Friendly, PDF & Email