diff --git a/plugins/jenkins/static/client.js b/plugins/jenkins/static/client.js new file mode 100644 index 0000000..41062b9 --- /dev/null +++ b/plugins/jenkins/static/client.js @@ -0,0 +1,123 @@ +plugins.jenkins = { + + html: null, + + start: function() { + html = ('

Jenkins Builds

    '); + $('div#body').append(html); + }, + + queue: 0, + + receiveData: function(data) { + + // Check for a single message (is not array?) + if (!$.isArray(data)) { + data = [data]; + } + + var container = $('div#jenkins ol'); + + // Save old queue count for comparison later + var oldqueue = this.queue; + this.queue = 0; + + for (c in data) { + var content = data[c]; + + if (content == 1) { + // Ignore (this is a placeholder) + continue; + } + + var id = 'build-'+content.id; + var status = content.status; + var statusclass = 'status-'+status; + var name = content.name; + var duration = content.duration; + var href = content.href; + + if (status == 'RUNNING') { + duration = status; + + // Update queue count (only running jobs have a queue property) + this.queue = content.queue; + } + + var link = $('').attr('href', href).html(name); + var dur = $('').html(duration); + + var node = $('
  1. ').attr('id', id).attr('class', statusclass).data('buildid', content.id).data('sort', content.sort); + node.html(link).append(dur); + node.hide(); + + // Check if this is an update (as the previous build already exists) + var exists = $('li#'+id, container); + if (exists.length) { + if (exists.attr('class') !== statusclass || status == 'RUNNING') { + console.log('replace'); + exists.replaceWith(node); + node.show(); + } + else { + console.log('no change'); + } + + continue; + } + + // Find the next highest build time and add after that + var buildsort = content.sort; + var next = null; + var nextid = null; + $('li', container).each(function() { + var check = $(this).data('sort'); + if (check > buildsort) { + if (next) { + if (next > check) { + next = check; + nextid = $(this).data('buildid'); + } + } else { + next = check; + nextid = $(this).data('buildid'); + } + } + }); + + if (next) { + // If found next highest, append after + $('li#build-'+nextid, container).after(node); + } else { + // Otherwise is highest, add to top + container.prepend(node); + } + + node.fadeIn(1000); + } + + if ($('li', container).length > 30) { + $('li:last', container).slideUp().remove(); + } + + // If queue length has changed, update it + if (oldqueue != this.queue) { + console.log('Update queue'); + + // Update queue + var qcontainer = $('div#jenkins h1 span.queue'); + if (!qcontainer.length) { + qcontainer = $(''); + $('div#jenkins h1').append(qcontainer); + } + + if (this.queue == 0) { + qcontainer.text(''); + } else if (this.queue == 1) { + qcontainer.text('['+this.queue+' build in queue]'); + } else { + qcontainer.text('['+this.queue+' builds in queue]'); + } + } + } +} diff --git a/plugins/jenkins/static/style.css b/plugins/jenkins/static/style.css new file mode 100644 index 0000000..ad00366 --- /dev/null +++ b/plugins/jenkins/static/style.css @@ -0,0 +1,86 @@ +div#jenkins li.urgent a { + color: #f00; +} + +.dashboard-fullscreen div#jenkins { + font-size: 32px; +} + +div#jenkins h1 span.queue { + margin-left: 2em; + display: inline-block; + color: #fff; +} + +.dashboard-fullscreen div#jenkins > ol > li { + padding-bottom: 10px; + padding-top: 10px; +} + +div#jenkins li.new span.status { + color: #f90; +} + +div#jenkins li { + padding: 2px 0; + border-bottom: 1px solid #999; + overflow: auto; + font-size: 110%; + margin-bottom: 1px; +} + +div#jenkins li a { + color: #fff; + padding: 3px 0; + display: inline-block; +/** text-shadow: 0 0 0.1em #fff; **/ +} + +div#jenkins li span.duration { + float: right; + background-color: #fff; + color: #333; + padding: 5px; + width: 20%; + text-align: center; +} +/** +div#jenkins li.status-RUNNING { + border: 3px solid #fff; +} + +div#jenkins li.status-ABORTED { + background-color: #666; +} + +div#jenkins li.status-FAILURE { + background-color: #f00; +} + +div#jenkins li.status-SUCCESS { + background-color: #0f0; +} + +div#jenkins li.status-SUCCESS a { + color: #333; + text-shadow: none; +} +**/ + +div#jenkins li.status-RUNNING { +/** border: 3px solid #666;**/ +} + +div#jenkins li.status-ABORTED span.duration { + background-color: #666; + color: #fff; +} + +div#jenkins li.status-FAILURE span.duration { + background-color: #f00; + color: #fff; +} + +div#jenkins li.status-SUCCESS span.duration { + background-color: #0f0; +} diff --git a/plugins/jenkins/update.py b/plugins/jenkins/update.py new file mode 100644 index 0000000..4c1bab2 --- /dev/null +++ b/plugins/jenkins/update.py @@ -0,0 +1,137 @@ +import ConfigParser +import json +import logging +import os +import requests +import time +import urllib + +import jenkinsapi +from jenkinsapi.jenkins import Jenkins + +CONFIG = {} + +def setup_config(): + global CONFIG + + # Load global config file + path = os.path.join(os.path.dirname(__file__), '../../config.ini') + con = ConfigParser.ConfigParser() + con.readfp(open(path)) + + host = con.get('general', 'host') + port = con.get('general', 'port') + CONFIG['host'] = '%s:%s' % (host, port) + + # Load module config file + path = os.path.join(os.path.dirname(__file__), 'config.ini') + con = ConfigParser.ConfigParser() + con.readfp(open(path)) + + CONFIG['jenkins'] = con.get('jenkins', 'host') + + +# Generic push to dashboard function +def dashboard_push_data(plugin, data, multiple = False): + global CONFIG + + d = [] + if multiple: + d.append(('multiple', 1)) + for item in data: + j = json.dumps(item) + d.append(('data', j)) + else: + j = json.dumps(data) + d.append(('data', j)) + + r = requests.post('http://%s/update/%s?%s' % (CONFIG['host'], plugin, urllib.urlencode(d))) + + + +# Main code +def main(): + global CONFIG + setup_config() + + server = Jenkins(CONFIG['jenkins']) + + #print(dir(j['moodle'])) + + jobs = ['moodle', 'totara'] + + # Run forever + while 1: + + try: + queueitems = [] + for jobname in jobs: + job = server[jobname] + + # Get queue length + if job.is_queued(): + q = server.get_queue() + queue = q.get_queue_items_for_job(jobname) + for qitem in queue: + queueitems.append(qitem.params.replace('\nBRANCH=', '')) + + queue = len(queueitems) + print('Queue length: %d' % queue) + if queue: + print('Queue: %s' % queueitems) + + builddata = [] + for jobname in jobs: + job = server[jobname] + + print('Job: %s' % jobname) + print('Is running build: %s' % ('yes' if job.is_running() else 'no')) + builds = job.get_build_ids() + + bcount = 0 + for bid in builds: + bcount += 1 + if bcount > 10: + break + + build = job.get_build(bid) + + name = build.name.split(' ') + namestr = name[0] + if len(name) > 2: + namestr = name[2] + + if build.is_running(): + status = 'RUNNING' + else: + status = build.get_status() + + duration = str(build.get_duration()).split('.')[0] + + data = {} + data['id'] = jobname+'_'+str(bid) + data['starttime'] = str(build.get_timestamp()) + data['sort'] = data['starttime'] + '_' + data['id'] + data['name'] = namestr + data['status'] = status + data['duration'] = duration + data['href'] = build.get_result_url() + + # If this build is running, note queue length + if status == 'RUNNING': + data['queue'] = queue + + print(data) + + builddata.append(data) + + dashboard_push_data('jenkins', builddata, True) + + except Exception as e: + print 'Exceptional exception!' + logging.exception(e) + + time.sleep(30) + +if __name__ == "__main__": + main()