Index: appengine_apps/chromium_status/appengine_module/chromium_status/event_push.py |
diff --git a/appengine_apps/chromium_status/appengine_module/chromium_status/event_push.py b/appengine_apps/chromium_status/appengine_module/chromium_status/event_push.py |
deleted file mode 100644 |
index 54e32ab414901e9b35ec70cb40cf2d0ac7a76cc1..0000000000000000000000000000000000000000 |
--- a/appengine_apps/chromium_status/appengine_module/chromium_status/event_push.py |
+++ /dev/null |
@@ -1,239 +0,0 @@ |
-# Copyright (c) 2011 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""StatusPush handler from the buildbot master. |
- |
-See event_sample.txt for event format examples. |
-""" |
- |
-import datetime |
-import json |
-import logging |
-import time |
- |
-from google.appengine.api.labs import taskqueue |
-from google.appengine.ext import db |
-from google.appengine.ext.db import polymodel |
-from google.appengine.runtime import DeadlineExceededError |
-from google.appengine.runtime.apiproxy_errors import CapabilityDisabledError |
- |
-from appengine_module.chromium_status.base_page import BasePage |
- |
-from appengine_module.chromium_status import utils |
- |
- |
-class Event(polymodel.PolyModel): |
- """Base class for every events pushed by buildbot's StatusPush.""" |
- eventid = db.IntegerProperty(required=True, indexed=False) |
- projectname = db.StringProperty(required=True) |
- projectstarted = db.DateTimeProperty(required=True) |
- created = db.DateTimeProperty(auto_now_add=True) |
- |
- |
-class Change(Event): |
- """Buildbot detected a change""" |
- comments = db.TextProperty() |
- files = db.StringListProperty() |
- # Buildbot's master change number in changes.pck |
- number = db.IntegerProperty(required=True) |
- revision = db.StringProperty(indexed=False) |
- when = db.DateTimeProperty(required=True) |
- who = db.StringProperty(indexed=False) |
- revlink = db.StringProperty(indexed=False) |
- |
- |
-class Build(Event): |
- """Buildbot completed a build""" |
- buildername = db.StringProperty(required=True) |
- buildnumber = db.IntegerProperty(required=True) |
- finished = db.DateTimeProperty(indexed=False) |
- reason = db.TextProperty() |
- results = db.IntegerProperty(indexed=False) |
- revision = db.StringProperty() |
- slave = db.StringProperty(indexed=False) |
- text = db.StringListProperty() |
- |
- |
-class BuildStep(Event): |
- """Buildbot completed a build step""" |
- buildername = db.StringProperty(required=True) |
- buildnumber = db.IntegerProperty(required=True) |
- finished = db.DateTimeProperty(indexed=False) |
- # TODO(maruel): Require it ASAP |
- stepnumber = db.IntegerProperty(required=False, indexed=False) |
- stepname = db.StringProperty(required=True) |
- text = db.StringListProperty() |
- results = db.IntegerProperty(indexed=False) |
- |
- |
-def ModelToStr(obj): |
- """Converts a model to a human readable string, for debugging""" |
- assert isinstance(obj, db.Model) |
- out = [obj.__class__.__name__] |
- for k in obj.properties(): |
- if k.startswith('_'): |
- continue |
- out.append(' %s: %s' % (k, str(getattr(obj, k)))) |
- return '\n'.join(out) |
- |
- |
-def onChangeAdded(packet): |
- change = packet['payload']['change'] |
- obj = Change.gql( |
- 'WHERE projectname = :1 AND projectstarted = :2 AND number = :3', |
- packet['project'], packet['projectstarted'], |
- change['number']).get() |
- if obj: |
- logging.info('Received duplicate %s/%s event for %s %s' % |
- (packet['event'], packet['eventid'], obj.__class__.__name__, |
- obj.number)) |
- return None |
- return Change( |
- eventid=packet['id'], |
- projectname=packet['project'], |
- projectstarted=packet['projectstarted'], |
- comments=change['comments'], |
- files=change['files'], |
- number=change['number'], |
- revision=change['revision'], |
- # TODO(maruel): Timezones |
- when=datetime.datetime.fromtimestamp(change['when']), |
- who=change['who'], |
- revlink=change['revlink']) |
- |
- |
-def onBuildFinished(packet): |
- build = packet['payload']['build'] |
- obj = Build.gql( |
- 'WHERE projectname = :1 AND projectstarted = :2 ' |
- 'AND buildername = :3 AND buildnumber = :4', |
- packet['project'], packet['projectstarted'], |
- build['builderName'], build['number']).get() |
- if obj: |
- logging.info('Received duplicate %s/%d event for %s %s' % |
- (packet['event'], packet['id'], obj.__class__.__name__, |
- obj.build_number)) |
- return None |
- # properties is an array of 3 values, keep the first 2 in a dict. |
- props = dict(((i[0], i[1]) for i in build['properties'])) |
- return Build( |
- eventid=packet['id'], |
- projectname=packet['project'], |
- projectstarted=packet['projectstarted'], |
- buildername=build['builderName'], |
- buildnumber=build['number'], |
- # TODO(maruel): Timezones |
- finished=datetime.datetime.fromtimestamp(build['times'][1]), |
- reason=build['reason'], |
- results=build.get('results', 0), |
- revision=props.get('got_revision', props.get('revision', None)), |
- slave=build['slave'], |
- text=build['text']) |
- |
- |
-def onStepFinished(packet): |
- payload = packet['payload'] |
- step = payload['step'] |
- # properties is an array of 3 values, keep the first 2 in a dict. |
- props = dict(((i[0], i[1]) for i in payload['properties'])) |
- obj = BuildStep.gql( |
- 'WHERE projectname = :1 AND projectstarted = :2 ' |
- 'AND buildername = :3 AND buildnumber = :4 AND stepname = :5', |
- packet['project'], packet['projectstarted'], |
- props['buildername'], props['buildnumber'], |
- step['name']).get() |
- if obj: |
- logging.info('Received duplicate %s/%d event for %s %s' % |
- (packet['event'], packet['id'], obj.__class__.__name__, |
- obj.buildnumber)) |
- return None |
- return BuildStep( |
- eventid=packet['id'], |
- projectname=packet['project'], |
- projectstarted=packet['projectstarted'], |
- buildername=props['buildername'], |
- buildnumber=props['buildnumber'], |
- # TODO(maruel): Send step number |
- #number=??? |
- stepname=step['name'], |
- text=step['text'], |
- results=step.get('results', (0,))[0]) |
- |
- |
-SUPPORTED_EVENTS = { |
- 'changeAdded': onChangeAdded, |
- 'buildFinished': onBuildFinished, |
- 'stepFinished': onStepFinished, |
-} |
- |
- |
-class StatusReceiver(BasePage): |
- """Buildbot's HttpStatusPush event receiver""" |
- @utils.requires_write_access |
- def post(self): |
- self.response.headers['Content-Type'] = 'text/plain' |
- try: |
- # TODO(maruel): Safety check, pwd? |
- packets = json.loads(self.request.POST['packets']) |
- # A list of packets should have been submitted, store each event. |
- objs = [] |
- for index in xrange(len(packets)): |
- packet = packets[index] |
- # To simplify getKwargs. |
- packet['projectname'] = packet['project'] |
- # TODO(maruel): Timezones |
- ts = time.strptime(packet['started'].split('.', 1)[0], |
- '%Y-%m-%d %H:%M:%S') |
- packet['projectstarted'] = ( |
- datetime.datetime.fromtimestamp(time.mktime(ts))) |
- packet['eventid'] = packet['id'] |
- if not 'payload' in packet: |
- packet['payload'] = json.loads(packet['payload_json']) |
- functor = SUPPORTED_EVENTS.get(packet['event'], None) |
- if not functor: |
- continue |
- obj = functor(packet) |
- if obj: |
- objs.append(obj) |
- # Save all the objects at once to reduce the number of rpc. |
- db.put(objs) |
- except CapabilityDisabledError: |
- logging.info('CapabilityDisabledError: read-only datastore') |
- self.response.out.write('CapabilityDisabledError: read-only datastore') |
- self.error(500) |
- return |
- except DeadlineExceededError: |
- logging.info('DeadlineExceededError') |
- self.response.out.write('DeadlineExceededError') |
- self.error(500) |
- return |
- taskqueue.add(url='/restricted/status-processor') |
- self.response.out.write('Success') |
- |
- |
-class RecentEvents(BasePage): |
- """Returns the most recent events. |
- |
- Mostly for testing""" |
- @utils.requires_read_access |
- def get(self): |
- self.response.headers['Content-Type'] = 'text/plain' |
- limit = int(self.request.GET.get('limit', 25)) |
- nb = 0 |
- for obj in Event.gql('ORDER BY created DESC LIMIT %d' % limit): |
- self.response.out.write('%s\n\n' % ModelToStr(obj)) |
- nb += 1 |
- logging.debug('RecentEvents(limit=%d) got %d' % (limit, nb)) |
- |
- |
-class StatusProcessor(BasePage): |
- """TODO(maruel): Implement http://crbug.com/21801 |
- |
- i.e. implement GateKeeper on appengine, close the tree when the buildbot |
- master shuts down, etc.""" |
- |
- @utils.requires_work_queue_login |
- def post(self): |
- self.response.headers['Content-Type'] = 'text/plain' |
- self.response.out.write('Success') |