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:
parent
6aea36dd35
commit
8dbd56aff0
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue