Changeset 107386 in webkit
- Timestamp:
- Feb 10, 2012 1:56:08 AM (12 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 6 edited
- 1 copied
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/ChangeLog
r107337 r107386 1 2012-02-10 Ryosuke Niwa <rniwa@webkit.org> 2 3 Perf-o-matic should process reports in background 4 https://bugs.webkit.org/show_bug.cgi?id=78309 5 6 Reviewed by Hajime Morita. 7 8 Split the logic to create Build, Test, and TestResult objects from ReportHandler into ReportProcessHandler. 9 ReportHandler now merely creates ReportLog and schedules a task to process it. 10 11 Also added ReportLogHandler to manage stale ReportLogs. 12 13 * Websites/webkit-perf.appspot.com/app.yaml: 14 * Websites/webkit-perf.appspot.com/controller.py: 15 (schedule_manifest_update): 16 (schedule_dashboard_update): 17 (schedule_runs_update): 18 (CachedRunsHandler.get): 19 (schedule_report_process): 20 * Websites/webkit-perf.appspot.com/main.py: 21 * Websites/webkit-perf.appspot.com/merge_tests.html: Renamed from Websites/webkit-perf.appspot.com/merge_tests.yaml. 22 * Websites/webkit-perf.appspot.com/models.py: 23 (ReportLog): 24 (ReportLog._parsed_payload): 25 (ReportLog.get_value): 26 (ReportLog.results): 27 (ReportLog.builder): 28 (ReportLog.branch): 29 (ReportLog.platform): 30 (ReportLog.build_number): 31 (ReportLog.webkit_revision): 32 (ReportLog.chromium_revision): 33 (ReportLog._model_by_key_name_in_payload): 34 (ReportLog._integer_in_payload): 35 (ReportLog.timestamp): 36 * Websites/webkit-perf.appspot.com/report_handler.py: 37 (ReportHandler.post): 38 (ReportHandler._output): 39 (ReportHandler._results_are_valid): 40 (ReportHandler._results_are_valid._is_float_convertible): 41 (ReportHandler): 42 * Websites/webkit-perf.appspot.com/report_logs.html: Added. 43 * Websites/webkit-perf.appspot.com/report_logs_handler.py: Added. 44 (ReportLogsHandler): 45 (ReportLogsHandler.get): 46 (ReportLogsHandler.post): 47 (ReportLogsHandler._error): 48 * Websites/webkit-perf.appspot.com/report_process_handler.py: Copied from Websites/webkit-perf.appspot.com/report_handler.py. 49 (ReportProcessHandler): 50 (ReportProcessHandler.post): 51 (ReportProcessHandler._create_build_if_possible): 52 (ReportProcessHandler._create_build_if_possible.execute): 53 (ReportProcessHandler._add_test_if_needed): 54 1 55 2012-02-09 Ryosuke Niwa <rniwa@webkit.org> 2 56 -
trunk/Websites/webkit-perf.appspot.com/app.yaml
r107274 r107386 1 1 application: webkit-perf 2 version: 1 22 version: 13 3 3 runtime: python27 4 4 api_version: 1 -
trunk/Websites/webkit-perf.appspot.com/controller.py
r107337 r107386 65 65 66 66 def schedule_manifest_update(): 67 taskqueue.add(url='/api/test/update' )67 taskqueue.add(url='/api/test/update', name='manifest_update') 68 68 69 69 … … 83 83 84 84 def schedule_dashboard_update(): 85 taskqueue.add(url='/api/test/dashboard/update' )85 taskqueue.add(url='/api/test/dashboard/update', name='dashboard_update') 86 86 87 87 … … 101 101 102 102 def schedule_runs_update(test_id, branch_id, platform_id): 103 taskqueue.add(url='/api/test/runs/update', params={'id': test_id, 'branchid': branch_id, 'platformid': platform_id}) 103 taskqueue.add(url='/api/test/runs/update', name='runs_update_%d_%d_%d' % (test_id, branch_id, platform_id), 104 params={'id': test_id, 'branchid': branch_id, 'platformid': platform_id}) 104 105 105 106 … … 123 124 else: 124 125 schedule_runs_update(test_id, branch_id, platform_id) 126 127 128 def schedule_report_process(log): 129 taskqueue.add(url='/api/test/report/process', params={'id': log.key().id()}) -
trunk/Websites/webkit-perf.appspot.com/main.py
r107274 r107386 29 29 from report_handler import ReportHandler 30 30 from report_handler import AdminReportHandler 31 from report_process_handler import ReportProcessHandler 32 from report_logs_handler import ReportLogsHandler 31 33 from runs_handler import RunsHandler 32 34 from merge_tests_handler import MergeTestsHandler … … 35 37 ('/admin/report/?', AdminReportHandler), 36 38 ('/admin/merge-tests/?', MergeTestsHandler), 39 ('/admin/report-logs/?', ReportLogsHandler), 37 40 ('/admin/create/(.*)', CreateHandler), 38 41 ('/api/test/?', CachedManifestHandler), 39 42 ('/api/test/update', ManifestHandler), 40 43 ('/api/test/report/?', ReportHandler), 44 ('/api/test/report/process', ReportProcessHandler), 41 45 ('/api/test/runs/?', CachedRunsHandler), 42 46 ('/api/test/runs/update', RunsHandler), -
trunk/Websites/webkit-perf.appspot.com/models.py
r107337 r107386 29 29 30 30 import hashlib 31 import json 31 32 import re 32 33 34 from datetime import datetime 33 35 from google.appengine.ext import db 34 36 … … 122 124 headers = db.TextProperty() 123 125 payload = db.TextProperty() 126 commit = db.BooleanProperty() 127 128 def _parsed_payload(self): 129 if self.__dict__.get('_parsed') == None: 130 try: 131 self._parsed = json.loads(self.payload) 132 except ValueError: 133 self._parsed = False 134 return self._parsed 135 136 def get_value(self, keyName): 137 if not self._parsed_payload(): 138 return None 139 return self._parsed.get(keyName, '') 140 141 def results(self): 142 return self.get_value('results') 143 144 def builder(self): 145 return self._model_by_key_name_in_payload(Builder, 'builder-name') 146 147 def branch(self): 148 return self._model_by_key_name_in_payload(Branch, 'branch') 149 150 def platform(self): 151 return self._model_by_key_name_in_payload(Platform, 'platform') 152 153 def build_number(self): 154 return self._integer_in_payload('build-number') 155 156 def webkit_revision(self): 157 return self._integer_in_payload('webkit-revision') 158 159 def chromium_revision(self): 160 return self._integer_in_payload('chromium-revision') 161 162 def _model_by_key_name_in_payload(self, model, keyName): 163 key = self.get_value(keyName) 164 if not key: 165 return None 166 return model.get_by_key_name(key) 167 168 def _integer_in_payload(self, keyName): 169 try: 170 return int(self.get_value(keyName)) 171 except ValueError: 172 return None 173 174 def timestamp(self): 175 try: 176 return datetime.fromtimestamp(self._integer_in_payload('timestamp')) 177 except TypeError: 178 return None 179 except ValueError: 180 return None 124 181 125 182 -
trunk/Websites/webkit-perf.appspot.com/report_handler.py
r107274 r107386 33 33 import json 34 34 import re 35 import time36 35 from datetime import datetime 37 36 38 from controller import schedule_runs_update39 from controller import schedule_dashboard_update40 from controller import schedule_manifest_update41 from models import Builder42 from models import Branch43 from models import Build44 from models import NumericIdHolder45 from models import Platform46 37 from models import ReportLog 47 from models import Test 48 from models import TestResult 49 from models import create_in_transaction_with_numeric_id_holder 38 from controller import schedule_report_process 50 39 51 40 … … 61 50 log.put() 62 51 52 self._encountered_error = False 53 63 54 try: 64 self._body = json.loads(self.request.body) 55 parsedPayload = json.loads(self.request.body) 56 password = parsedPayload.get('password', '') 65 57 except ValueError: 66 58 return self._output('Failed to parse the payload as a json. Report key: %d' % log.key().id()) 67 59 68 builder = self._model_by_key_name_in_body_or_error(Builder, 'builder-name')69 b ranch = self._model_by_key_name_in_body_or_error(Branch, 'branch')70 platform = self._model_by_key_name_in_body_or_error(Platform, 'platform')71 build_number = self._integer_in_body('build-number')72 timestamp = self._timestamp_in_body()73 revision = self._integer_in_body('webkit-revision')74 chromium_revision = self._integer_in_body('webkit-revision') if 'chromium-revision' in self._body else None60 builder = log.builder() 61 builder != None or self._output('No builder named "%s"' % log.get_value('builder-name')) 62 log.branch() != None or self._output('No branch named "%s"' % log.get_value('branch')) 63 log.platform() != None or self._output('No platform named "%s"' % log.get_value('platform')) 64 log.build_number() != None or self._output('Invalid build number "%s"' % log.get_value('build-number')) 65 log.timestamp() != None or self._output('Invalid timestamp "%s"' % log.get_value('timestamp')) 66 log.webkit_revision() != None or self._output('Invalid webkit revision "%s"' % log.get_value('webkit-revision')) 75 67 76 68 failed = False 77 if builder and not (self.bypass_authentication() or builder.authenticate( self._body.get('password', ''))):69 if builder and not (self.bypass_authentication() or builder.authenticate(password)): 78 70 self._output('Authentication failed') 79 failed = True80 71 81 if not self._results_are_valid( ):72 if not self._results_are_valid(log): 82 73 self._output("The payload doesn't contain results or results are malformed") 83 failed = True84 74 85 if not (builder and branch and platform and build_number and revision and timestamp) or failed:75 if self._encountered_error: 86 76 return 87 77 88 build = self._create_build_if_possible(builder, build_number, branch, platform, timestamp, revision, chromium_revision) 89 if not build: 90 return 91 92 def _float_or_none(dictionary, key): 93 value = dictionary.get(key) 94 if value: 95 return float(value) 96 return None 97 98 for test_name, result in self._body['results'].iteritems(): 99 test = self._add_test_if_needed(test_name, branch, platform) 100 schedule_runs_update(test.id, branch.id, platform.id) 101 if isinstance(result, dict): 102 TestResult(name=test_name, build=build, value=float(result['avg']), valueMedian=_float_or_none(result, 'median'), 103 valueStdev=_float_or_none(result, 'stdev'), valueMin=_float_or_none(result, 'min'), valueMax=_float_or_none(result, 'max')).put() 104 else: 105 TestResult(name=test_name, build=build, value=float(result)).put() 106 107 log = ReportLog.get(log.key()) 108 log.delete() 109 110 # We need to update dashboard and manifest because they are affected by the existance of test results 111 schedule_dashboard_update() 112 schedule_manifest_update() 113 114 return self._output('OK') 115 116 def _model_by_key_name_in_body_or_error(self, model, keyName): 117 key = self._body.get(keyName, '') 118 instance = key and model.get_by_key_name(key) 119 if not instance: 120 self._output('There are no %s named "%s"' % (model.__name__.lower(), key)) 121 return instance 122 123 def _integer_in_body(self, key): 124 value = self._body.get(key, '') 125 try: 126 return int(value) 127 except: 128 return self._output('Invalid %s: "%s"' % (key.replace('-', ' '), value)) 129 130 def _timestamp_in_body(self): 131 value = self._body.get('timestamp', '') 132 try: 133 return datetime.fromtimestamp(int(value)) 134 except: 135 return self._output('Failed to parse the timestamp: %s' % value) 78 log.commit = True 79 log.put() 80 schedule_report_process(log) 81 self._output("OK") 136 82 137 83 def _output(self, message): 84 self._encountered_error = True 138 85 self.response.out.write(message + '\n') 139 86 … … 141 88 return False 142 89 143 def _results_are_valid(self ):90 def _results_are_valid(self, log): 144 91 145 92 def _is_float_convertible(value): … … 149 96 except TypeError: 150 97 return False 98 except ValueError: 99 return False 151 100 152 if 'results' not in self._body or not isinstance(self._body['results'], dict):101 if not isinstance(log.results(), dict): 153 102 return False 154 103 155 for testResult in self._body['results'].values():104 for testResult in log.results().values(): 156 105 if isinstance(testResult, dict): 157 106 for value in testResult.values(): … … 166 115 return True 167 116 168 def _create_build_if_possible(self, builder, build_number, branch, platform, timestamp, revision, chromium_revision):169 key_name = builder.name + ':' + str(int(time.mktime(timestamp.timetuple())))170 171 def execute():172 build = Build.get_by_key_name(key_name)173 if build:174 return self._output('The build at %s already exists for %s' % (str(timestamp), builder.name))175 176 return Build(branch=branch, platform=platform, builder=builder, buildNumber=build_number,177 timestamp=timestamp, revision=revision, chromiumRevision=chromium_revision, key_name=key_name).put()178 return db.run_in_transaction(execute)179 180 def _add_test_if_needed(self, test_name, branch, platform):181 182 def execute(id):183 test = Test.get_by_key_name(test_name)184 returnValue = None185 if not test:186 test = Test(id=id, name=test_name, key_name=test_name)187 returnValue = test188 if branch.key() not in test.branches:189 test.branches.append(branch.key())190 if platform.key() not in test.platforms:191 test.platforms.append(platform.key())192 test.put()193 return returnValue194 return create_in_transaction_with_numeric_id_holder(execute) or Test.get_by_key_name(test_name)195 196 117 197 118 class AdminReportHandler(ReportHandler): -
trunk/Websites/webkit-perf.appspot.com/report_process_handler.py
r107331 r107386 31 31 from google.appengine.ext import db 32 32 33 import json34 import re35 33 import time 36 from datetime import datetime37 34 38 35 from controller import schedule_runs_update 39 36 from controller import schedule_dashboard_update 40 37 from controller import schedule_manifest_update 41 from models import Builder42 from models import Branch43 38 from models import Build 44 from models import NumericIdHolder45 from models import Platform46 39 from models import ReportLog 47 40 from models import Test … … 50 43 51 44 52 class Report Handler(webapp2.RequestHandler):45 class ReportProcessHandler(webapp2.RequestHandler): 53 46 def post(self): 54 47 self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' 55 48 56 headers = "\n".join([key + ': ' + value for key, value in self.request.headers.items()])49 log_id = int(self.request.get('id', 0)) 57 50 58 # Do as best as we can to remove the password 59 request_body_without_password = re.sub(r'"password"\s*:\s*".+?",', '', self.request.body) 60 log = ReportLog(timestamp=datetime.now(), headers=headers, payload=request_body_without_password) 61 log.put() 62 63 try: 64 self._body = json.loads(self.request.body) 65 except ValueError: 66 return self._output('Failed to parse the payload as a json. Report key: %d' % log.key().id()) 67 68 builder = self._model_by_key_name_in_body_or_error(Builder, 'builder-name') 69 branch = self._model_by_key_name_in_body_or_error(Branch, 'branch') 70 platform = self._model_by_key_name_in_body_or_error(Platform, 'platform') 71 build_number = self._integer_in_body('build-number') 72 timestamp = self._timestamp_in_body() 73 revision = self._integer_in_body('webkit-revision') 74 chromium_revision = self._integer_in_body('webkit-revision') if 'chromium-revision' in self._body else None 75 76 failed = False 77 if builder and not (self.bypass_authentication() or builder.authenticate(self._body.get('password', ''))): 78 self._output('Authentication failed') 79 failed = True 80 81 if not self._results_are_valid(): 82 self._output("The payload doesn't contain results or results are malformed") 83 failed = True 84 85 if not (builder and branch and platform and build_number and revision and timestamp) or failed: 86 return 87 88 build = self._create_build_if_possible(builder, build_number, branch, platform, timestamp, revision, chromium_revision) 89 if not build: 51 log = ReportLog.get_by_id(log_id) 52 if not log or not log.commit: 53 self.response.out.write("Not processed") 90 54 return 91 55 … … 96 60 return None 97 61 98 for test_name, result in self._body['results'].iteritems(): 62 branch = log.branch() 63 platform = log.platform() 64 build = self._create_build_if_possible(log, branch, platform) 65 66 for test_name, result in log.results().iteritems(): 99 67 test = self._add_test_if_needed(test_name, branch, platform) 100 68 schedule_runs_update(test.id, branch.id, platform.id) … … 112 80 schedule_manifest_update() 113 81 114 return self._output('OK')82 self.response.out.write('OK') 115 83 116 def _model_by_key_name_in_body_or_error(self, model, keyName): 117 key = self._body.get(keyName, '') 118 instance = key and model.get_by_key_name(key) 119 if not instance: 120 self._output('There are no %s named "%s"' % (model.__name__.lower(), key)) 121 return instance 122 123 def _integer_in_body(self, key): 124 value = self._body.get(key, '') 125 try: 126 return int(value) 127 except: 128 return self._output('Invalid %s: "%s"' % (key.replace('-', ' '), value)) 129 130 def _timestamp_in_body(self): 131 value = self._body.get('timestamp', '') 132 try: 133 return datetime.fromtimestamp(int(value)) 134 except: 135 return self._output('Failed to parse the timestamp: %s' % value) 136 137 def _output(self, message): 138 self.response.out.write(message + '\n') 139 140 def bypass_authentication(self): 141 return False 142 143 def _results_are_valid(self): 144 145 def _is_float_convertible(value): 146 try: 147 float(value) 148 return True 149 except TypeError: 150 return False 151 152 if 'results' not in self._body or not isinstance(self._body['results'], dict): 153 return False 154 155 for testResult in self._body['results'].values(): 156 if isinstance(testResult, dict): 157 for value in testResult.values(): 158 if not _is_float_convertible(value): 159 return False 160 if 'avg' not in testResult: 161 return False 162 continue 163 if not _is_float_convertible(testResult): 164 return False 165 166 return True 167 168 def _create_build_if_possible(self, builder, build_number, branch, platform, timestamp, revision, chromium_revision): 169 key_name = builder.name + ':' + str(int(time.mktime(timestamp.timetuple()))) 84 def _create_build_if_possible(self, log, branch, platform): 85 builder = log.builder() 86 key_name = builder.name + ':' + str(int(time.mktime(log.timestamp().timetuple()))) 170 87 171 88 def execute(): 172 89 build = Build.get_by_key_name(key_name) 173 90 if build: 174 return self._output('The build at %s already exists for %s' % (str(timestamp), builder.name))91 return build 175 92 176 return Build(branch=branch, platform=platform, builder=builder, buildNumber=build_number, 177 timestamp=timestamp, revision=revision, chromiumRevision=chromium_revision, key_name=key_name).put() 93 return Build(branch=branch, platform=platform, builder=builder, buildNumber=log.build_number(), 94 timestamp=log.timestamp(), revision=log.webkit_revision(), chromiumRevision=log.chromium_revision(), 95 key_name=key_name).put() 178 96 return db.run_in_transaction(execute) 179 97 … … 193 111 return returnValue 194 112 return create_in_transaction_with_numeric_id_holder(execute) or Test.get_by_key_name(test_name) 195 196 197 class AdminReportHandler(ReportHandler):198 def bypass_authentication(self):199 return True
Note: See TracChangeset
for help on using the changeset viewer.