Add JSON status endpoint.

Can be used for nifty ajax-style status pages.

Add optional description field to pipeline.

Change-Id: If5db3f6945f65f038833cbf9c783de5ffef63b49
Reviewed-on: https://review.openstack.org/18579
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
This commit is contained in:
James E. Blair 2012-12-22 10:55:10 -08:00 committed by Jenkins
parent 6aea36dd35
commit 8dbd56aff0
4 changed files with 81 additions and 1 deletions

View File

@ -166,6 +166,10 @@ explanation of each of the parameters::
This is used later in the project definition to indicate what jobs This is used later in the project definition to indicate what jobs
should be run for events in the pipeline. should be run for events in the pipeline.
**description**
This is an optional field that may be used to provide a textual
description of the pipeline.
**manager** **manager**
There are currently two schemes for managing pipelines: There are currently two schemes for managing pipelines:

View File

@ -49,9 +49,9 @@ class JenkinsCallback(threading.Thread):
def app(self, environ, start_response): def app(self, environ, start_response):
request = Request(environ) request = Request(environ)
start_response('200 OK', [('content-type', 'text/html')])
if request.path == '/jenkins_endpoint': if request.path == '/jenkins_endpoint':
self.jenkins_endpoint(request) self.jenkins_endpoint(request)
start_response('200 OK', [('content-type', 'text/html')])
return ['Zuul good.'] return ['Zuul good.']
elif request.path == '/status': elif request.path == '/status':
try: try:
@ -59,8 +59,19 @@ class JenkinsCallback(threading.Thread):
except: except:
self.log.exception("Exception formatting status:") self.log.exception("Exception formatting status:")
raise raise
start_response('200 OK', [('content-type', 'text/html')])
return [ret]
elif request.path == '/status.json':
try:
ret = self.jenkins.sched.formatStatusJSON()
except:
self.log.exception("Exception formatting status:")
raise
start_response('200 OK', [('content-type', 'application/json'),
('Access-Control-Allow-Origin', '*')])
return [ret] return [ret]
else: else:
start_response('200 OK', [('content-type', 'text/html')])
return ['Zuul good.'] return ['Zuul good.']
def jenkins_endpoint(self, request): def jenkins_endpoint(self, request):

View File

@ -27,6 +27,7 @@ class Pipeline(object):
"""A top-level pipeline such as check, gate, post, etc.""" """A top-level pipeline such as check, gate, post, etc."""
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self.description = None
self.job_trees = {} # project -> JobTree self.job_trees = {} # project -> JobTree
self.manager = None self.manager = None
self.queues = [] self.queues = []
@ -186,6 +187,24 @@ class Pipeline(object):
ret += self.formatStatus(head, html=True) ret += self.formatStatus(head, html=True)
return ret return ret
def formatStatusJSON(self):
j_pipeline = dict(name=self.name,
description=self.description)
j_queues = []
j_pipeline['change_queues'] = j_queues
for queue in self.queues:
j_queue = dict(name=queue.name)
j_queues.append(j_queue)
j_queue['heads'] = []
for head in queue.getHeads():
j_changes = []
c = head
while c:
j_changes.append(self.formatChangeJSON(c))
c = c.change_behind
j_queue['heads'].append(j_changes)
return j_pipeline
def formatStatus(self, changeish, indent=0, html=False): def formatStatus(self, changeish, indent=0, html=False):
indent_str = ' ' * indent indent_str = ' ' * indent
ret = '' ret = ''
@ -224,6 +243,29 @@ class Pipeline(object):
ret += self.formatStatus(changeish.change_behind, indent + 2, html) ret += self.formatStatus(changeish.change_behind, indent + 2, html)
return ret return ret
def formatChangeJSON(self, changeish):
ret = {}
if hasattr(changeish, 'url') and changeish.url is not None:
ret['url'] = changeish.url
ret['id'] = changeish._id()
ret['project'] = changeish.project.name
ret['jobs'] = []
for job in self.getJobs(changeish):
build = changeish.current_build_set.getBuild(job.name)
if build:
result = build.result
url = build.url
else:
result = None
url = None
ret['jobs'].append(
dict(
name=job.name,
url=url,
result=result,
voting=job.voting))
return ret
class ChangeQueue(object): class ChangeQueue(object):
"""DependentPipelines have multiple parallel queues shared by """DependentPipelines have multiple parallel queues shared by

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import json
import logging import logging
import os import os
import pickle import pickle
@ -82,6 +83,7 @@ class Scheduler(threading.Thread):
for conf_pipeline in data.get('pipelines', []): for conf_pipeline in data.get('pipelines', []):
pipeline = Pipeline(conf_pipeline['name']) pipeline = Pipeline(conf_pipeline['name'])
pipeline.description = conf_pipeline.get('description')
manager = globals()[conf_pipeline['manager']](self, pipeline) manager = globals()[conf_pipeline['manager']](self, pipeline)
pipeline.setManager(manager) pipeline.setManager(manager)
@ -407,6 +409,27 @@ class Scheduler(threading.Thread):
ret += '</pre></html>' ret += '</pre></html>'
return ret return ret
def formatStatusJSON(self):
data = {}
if self._pause:
ret = '<p><b>Queue only mode:</b> preparing to '
if self._reconfigure:
ret += 'reconfigure'
if self._exit:
ret += 'exit'
ret += ', queue length: %s' % self.trigger_event_queue.qsize()
ret += '</p>'
data['message'] = ret
pipelines = []
data['pipelines'] = pipelines
keys = self.pipelines.keys()
keys.sort()
for key in keys:
pipeline = self.pipelines[key]
pipelines.append(pipeline.formatStatusJSON())
return json.dumps(data)
class BasePipelineManager(object): class BasePipelineManager(object):
log = logging.getLogger("zuul.BasePipelineManager") log = logging.getLogger("zuul.BasePipelineManager")