1 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import json 6 import time 7 8 from google.appengine.api import urlfetch 9 import webapp2 10 11 from base import bigquery 12 from base import constants 13 from common import query_filter 14 15 16 class Query(webapp2.RequestHandler): 17 18 def get(self): 19 urlfetch.set_default_fetch_deadline(60) 20 21 try: 22 filters = query_filter.Filters(self.request) 23 except ValueError as e: 24 self.response.headers['Content-Type'] = 'application/json' 25 self.response.out.write({'error': str(e)}) 26 return 27 query_results = _QueryEvents(bigquery.BigQuery(), **filters) 28 trace_events = list(_ConvertQueryEventsToTraceEvents(query_results)) 29 30 self.response.headers['Content-Type'] = 'application/json' 31 self.response.out.write(json.dumps(trace_events, separators=(',', ':'))) 32 33 34 def _QueryEvents(bq, **filters): 35 start_time = filters.get( 36 'start_time', time.time() - constants.DEFAULT_HISTORY_DURATION_SECONDS) 37 query_start_time_ms = int(start_time * 1000) 38 query_start_time_us = int(start_time * 1000000) 39 40 fields = ( 41 'name', 42 'GREATEST(INTEGER(start_time), %d) AS start_time_us' % 43 query_start_time_us, 44 'INTEGER(end_time) AS end_time_us', 45 'builder', 46 'configuration', 47 'hostname', 48 'status', 49 'url', 50 ) 51 52 tables = (constants.BUILDS_TABLE, constants.CURRENT_BUILDS_TABLE) 53 tables = ['[%s.%s@%d-]' % (constants.DATASET, table, query_start_time_ms) 54 for table in tables] 55 56 conditions = [] 57 conditions.append('NOT LOWER(name) CONTAINS "trigger"') 58 conditions.append('end_time - start_time >= 1000000') 59 conditions.append('end_time > %d' % query_start_time_us) 60 for filter_name, filter_values in filters.iteritems(): 61 if not isinstance(filter_values, list): 62 continue 63 64 if isinstance(filter_values[0], int): 65 filter_values = map(str, filter_values) 66 elif isinstance(filter_values[0], basestring): 67 # QueryFilter handles string validation. Assume no quotes in string. 68 filter_values = ['"%s"' % v for v in filter_values] 69 else: 70 raise NotImplementedError() 71 72 conditions.append('%s IN (%s)' % (filter_name, ','.join(filter_values))) 73 74 query = ('SELECT %s ' % ','.join(fields) + 75 'FROM %s ' % ','.join(tables) + 76 'WHERE %s ' % ' AND '.join(conditions) + 77 'ORDER BY builder, start_time') 78 return bq.QuerySync(query) 79 80 81 def _ConvertQueryEventsToTraceEvents(events): 82 for row in events: 83 event_start_time_us = int(row['f'][1]['v']) 84 event_end_time_us = int(row['f'][2]['v']) 85 86 status = row['f'][6]['v'] 87 if status: 88 status = int(status) 89 # TODO: Use constants from update/common/buildbot/__init__.py. 90 if status == 0: 91 color_name = 'cq_build_passed' 92 elif status == 1: 93 color_name = 'cq_build_warning' 94 elif status == 2: 95 color_name = 'cq_build_failed' 96 elif status == 4: 97 color_name = 'cq_build_exception' 98 elif status == 5: 99 color_name = 'cq_build_abandoned' 100 else: 101 color_name = 'cq_build_running' 102 103 yield { 104 'name': row['f'][0]['v'], 105 'pid': row['f'][4]['v'], 106 'tid': '%s [%s]' % (row['f'][3]['v'], row['f'][5]['v']), 107 'ph': 'X', 108 'ts': event_start_time_us, 109 'dur': event_end_time_us - event_start_time_us, 110 'cname': color_name, 111 'args': { 112 'url': row['f'][7]['v'], 113 }, 114 } 115