blob: fcec9b8cb0e7632d9cb647a4dc1bc9c2131c573e [file] [log] [blame]
"""
Status information for the CI infrastructure, for use by the dashboard.
"""
import threading
import time
import traceback
import StringIO
from llvmlab import util
import buildbot.statusclient
class BuildStatus(util.simple_repr_mixin):
@staticmethod
def fromdata(data):
version = data['version']
if version not in (0, 1):
raise ValueError, "Unknown version"
if version == 0:
slave = None
else:
slave = data['slave']
return BuildStatus(data['name'], data['number'], data['source_stamp'],
data['result'], data['start_time'], data['end_time'],
slave)
def todata(self):
return { 'version' : 1,
'name' : self.name,
'number' : self.number,
'source_stamp' : self.source_stamp,
'result' : self.result,
'start_time' : self.start_time,
'end_time' : self.end_time,
'slave' : self.slave }
def __init__(self, name, number, source_stamp,
result, start_time, end_time, slave):
self.name = name
self.number = number
self.source_stamp = source_stamp
self.result = result
self.start_time = start_time
self.end_time = end_time
self.slave = slave
class StatusMonitor(threading.Thread):
def __init__(self, app, status):
threading.Thread.__init__(self)
self.daemon = True
self.app = app
self.status = status
def run(self):
while 1:
try:
self.read_events()
except:
# Log this failure.
os = StringIO.StringIO()
print >>os, "*** ERROR: failure in buildbot monitor"
print >>os, "\n-- Traceback --"
traceback.print_exc(file = os)
self.app.logger.error(os.getvalue())
# Sleep for a while, then restart.
time.sleep(60)
def read_events(self):
# Constantly read events from the status client.
while 1:
for event in self.status.statusclient.pull_events():
# Log the event (for debugging).
self.status.event_log.append((time.time(), event))
self.status.event_log = self.status.event_log[-100:]
kind = event[0]
if kind == 'added_builder':
name = event[1]
if name not in self.status.builders:
self.status.builders[name] = []
self.status.build_map[name] = {}
elif kind == 'removed_builder':
name = event[1]
if name in self.status.builders:
self.status.builders.pop(name)
self.status.build_map.pop(name)
elif kind == 'reset_builder':
name = event[1]
self.status.builders[name] = []
self.status.build_map[name] = {}
elif kind == 'invalid_build':
_,name,id = event
build = self.status.build_map[name].get(id)
if build is not None:
self.status.builders[name].remove(build)
self.status.build_map[name].pop(id)
elif kind in ('add_build', 'completed_build'):
_,name,id = event
build = self.status.build_map[name].get(id)
add_build = False
if build is None:
add_build = True
build = BuildStatus(name, id, None, None, None, None,
None)
# Get the build information.
try:
res = self.status.statusclient.get_json_result((
'builders', name, 'builds', str(build.number)))
except:
res = None
if res:
build.result = res['results']
if 'sourceStamps' in res:
build.source_stamp = res['sourceStamps'][0]['revision']
else:
build.source_stamp = res['sourceStamp']['revision']
build.start_time = res['times'][0]
build.end_time = res['times'][1]
build.slave = res['slave']
if add_build:
# Add to the builds list, maintaining order.
self.status.build_map[name][id] = build
builds = self.status.builders[name]
builds.append(build)
if (len(builds) > 1 and
build.number < builds[-2].number):
builds.sort(key = lambda b: b.number)
else:
self.app.logger.warning("unknown event '%r'" % (event,))
# FIXME: Don't save this frequently, we really just want to
# checkpoint and make sure we save on restart.
self.app.save_status()
time.sleep(.1)
class Status(util.simple_repr_mixin):
@staticmethod
def fromdata(data):
version = data['version']
if version != 0:
raise ValueError, "Unknown version"
sc = data.get('statusclient')
if sc:
sc = buildbot.statusclient.StatusClient.fromdata(sc)
return Status(data['master_url'],
dict((name, [BuildStatus.fromdata(b)
for b in builds])
for name,builds in data['builders']),
sc)
def todata(self):
return { 'version' : 0,
'master_url' : self.master_url,
'builders' : [(name, [b.todata()
for b in builds])
for name,builds in self.builders.items()],
'statusclient' : self.statusclient.todata() }
def __init__(self, master_url, builders, statusclient = None):
self.master_url = master_url
self.builders = builders
if statusclient is None and master_url:
statusclient = buildbot.statusclient.StatusClient(master_url)
self.statusclient = statusclient
# Transient data.
self.event_log = []
self.build_map = dict((name, dict((b.number, b)
for b in builds))
for name,builds in self.builders.items())
def start_monitor(self, app):
if self.statusclient:
self.statusclient.logger = app.logger
monitor = StatusMonitor(app, self)
monitor.start()
return monitor