Skip to content
This repository has been archived by the owner on May 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #7 from jtilander/fix_for_folders
Browse files Browse the repository at this point in the history
Fixed exporter for jobs that are nested in subfolders in jenkins.
  • Loading branch information
SqiSch authored Feb 16, 2017
2 parents 4c5151c + 7bdd313 commit 4f5c950
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 25 deletions.
8 changes: 5 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY requirements.txt /usr/src/app
COPY jenkins_exporter.py /usr/src/app
RUN pip install --no-cache-dir -r requirements.txt

COPY jenkins_exporter.py /usr/src/app

EXPOSE 9118
ENV JENKINS_SERVER=http://jenkins:8080 VIRTUAL_PORT=9118 DEBUG=0

ENTRYPOINT [ "python", "./jenkins_exporter.py" ]
CMD ["-j", "http://jenkins:8080", "-p", "9118"]
ENTRYPOINT [ "python", "-u", "./jenkins_exporter.py" ]
CMD []
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
IMAGENAME?=lovoo/jenkins_exporter
TAG?=latest
JENKINS_SERVER?=https://myjenkins

debug: image
docker run --rm -p 9118:9118 -e DEBUG=1 -e JENKINS_SERVER=$(JENKINS_SERVER) -e VIRTUAL_PORT=9118 $(IMAGENAME):$(TAG)

image:
docker build -t $(IMAGENAME):$(TAG) .

push: image
docker push $(IMAGENAME):$(TAG)


.PHONY: image push debug
71 changes: 49 additions & 22 deletions jenkins_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
import time
import requests
import argparse
from pprint import pprint

import os
from sys import exit
from prometheus_client import start_http_server
from prometheus_client.core import GaugeMetricFamily, REGISTRY

DEBUG = int(os.environ.get('DEBUG', '0'))


class JenkinsCollector(object):
# The build statuses we want to export about.
Expand All @@ -21,12 +25,15 @@ def __init__(self, target):

def collect(self):
# Request data from Jenkins
jenkins_data = self._request_data()
jobs = self._request_data()

self._setup_empty_prometheus_metrics()

for job in jenkins_data['jobs']:
for job in jobs:
name = job['name']
if DEBUG:
print "Found Job: %s" % name
pprint(job)
self._get_metrics(name, job)

for status in self.statuses:
Expand All @@ -38,16 +45,29 @@ def _request_data(self):
url = '{0}/api/json'.format(self._target)
jobs = "[number,timestamp,duration,actions[queuingDurationMillis,totalDurationMillis," \
"skipCount,failCount,totalCount,passCount]]"
tree = 'jobs[name,{0}]'.format(','.join([s + jobs for s in self.statuses]))
tree = 'jobs[name,url,{0}]'.format(','.join([s + jobs for s in self.statuses]))
params = {
'tree': tree,
}
# params = tree: jobs[name,lastBuild[number,timestamp,duration,actions[queuingDurationMillis...
response = requests.get(url, params=params)
if response.status_code != requests.codes.ok:
raise Exception('Response Status ({0}): {1}'.format(response.status_code, response.text))
result = response.json()
return result

def parsejobs(myurl):
# params = tree: jobs[name,lastBuild[number,timestamp,duration,actions[queuingDurationMillis...
response = requests.get(myurl, params=params)
if response.status_code != requests.codes.ok:
return[]
result = response.json()
if DEBUG:
pprint(result)

jobs = []
for job in result['jobs']:
if job['_class'] == 'com.cloudbees.hudson.plugins.folder.Folder':
jobs += parsejobs(job['url'] + '/api/json')
else:
jobs.append(job)
return jobs

return parsejobs(url)

def _setup_empty_prometheus_metrics(self):
# The metrics we want to export.
Expand Down Expand Up @@ -93,28 +113,29 @@ def _get_metrics(self, name, job):

def _add_data_to_prometheus_structure(self, status, status_data, job, name):
# If there's a null result, we want to pass.
if status_data.get('duration'):
if status_data.get('duration', 0):
self._prometheus_metrics[status]['duration'].add_metric([name], status_data.get('duration') / 1000.0)
if status_data.get('timestamp'):
if status_data.get('timestamp', 0):
self._prometheus_metrics[status]['timestamp'].add_metric([name], status_data.get('timestamp') / 1000.0)
if status_data.get('number'):
if status_data.get('number', 0):
self._prometheus_metrics[status]['number'].add_metric([name], status_data.get('number'))
actions_metrics = status_data.get('actions', [{}])
for metric in actions_metrics:
if metric.has_key('queuingDurationMillis') and metric.get('queuingDurationMillis'):
if metric.get('queuingDurationMillis', False):
self._prometheus_metrics[status]['queuingDurationMillis'].add_metric([name], metric.get('queuingDurationMillis') / 1000.0)
if metric.has_key('totalDurationMillis') and metric.get('totalDurationMillis'):
if metric.get('totalDurationMillis', False):
self._prometheus_metrics[status]['totalDurationMillis'].add_metric([name], metric.get('totalDurationMillis') / 1000.0)
if metric.has_key('skipCount') and metric.get('skipCount'):
if metric.get('skipCount', False):
self._prometheus_metrics[status]['skipCount'].add_metric([name], metric.get('skipCount'))
if metric.has_key('failCount') and metric.get('failCount'):
if metric.get('failCount', False):
self._prometheus_metrics[status]['failCount'].add_metric([name], metric.get('failCount'))
if metric.has_key('totalCount') and metric.get('totalCount'):
if metric.get('totalCount', False):
self._prometheus_metrics[status]['totalCount'].add_metric([name], metric.get('totalCount'))
# Calculate passCount by subtracting fails and skips from totalCount
passcount = metric.get('totalCount') - metric.get('failCount') - metric.get('skipCount')
self._prometheus_metrics[status]['passCount'].add_metric([name], passcount)


def parse_args():
parser = argparse.ArgumentParser(
description='jenkins exporter args jenkins address and port'
Expand All @@ -124,26 +145,32 @@ def parse_args():
metavar='jenkins',
required=False,
help='server url from the jenkins api',
default='http://jenkins:8080'
default=os.environ.get('JENKINS_SERVER', 'http://jenkins:8080')
)
parser.add_argument(
'-p', '--port',
metavar='port',
required=False,
type=int,
help='Listen to this port',
default=9118
default=int(os.environ.get('VIRTUAL_PORT', '9118'))
)
return parser.parse_args()

if __name__ == "__main__":

def main():
try:
args = parse_args()
port = int(args.port)
REGISTRY.register(JenkinsCollector(args.jenkins))
start_http_server(port)
print "Serving at port: ", port
while True: time.sleep(1)
print "Polling %s. Serving at port: %s" % (args.jenkins, port)
while True:
time.sleep(1)
except KeyboardInterrupt:
print(" Interrupted")
exit(0)


if __name__ == "__main__":
main()

0 comments on commit 4f5c950

Please sign in to comment.