Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(570)

Side by Side Diff: appengine/findit/handlers/build_failure.py

Issue 2026283002: [Findit] Adding logic to force try jobs regardless of bailout or previous results (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Addressing comments and rebase Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | appengine/findit/handlers/handlers_util.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 from collections import defaultdict 5 from collections import defaultdict
6 import copy
7 from datetime import datetime 6 from datetime import datetime
8 import logging
9 import os
10 7
11 from google.appengine.api import users 8 from google.appengine.api import users
12 9
13 from common import constants 10 from common import constants
14 from common.base_handler import BaseHandler 11 from common.base_handler import BaseHandler
15 from common.base_handler import Permission 12 from common.base_handler import Permission
16 from common.waterfall import failure_type 13 from common.waterfall import failure_type
17 from handlers import handlers_util 14 from handlers import handlers_util
18 from handlers import result_status 15 from handlers import result_status
19 from handlers.result_status import NO_TRY_JOB_REASON_MAP 16 from handlers.result_status import NO_TRY_JOB_REASON_MAP
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 'last_pass': test_result.get('last_pass'), 137 'last_pass': test_result.get('last_pass'),
141 'supported': supported, 138 'supported': supported,
142 'tests': group['tests'], 139 'tests': group['tests'],
143 'suspected_cls': group['suspected_cls'] 140 'suspected_cls': group['suspected_cls']
144 } 141 }
145 organized_suspected_cls.append(shared_result) 142 organized_suspected_cls.append(shared_result)
146 143
147 return organized_results 144 return organized_results
148 145
149 146
150 def _GetAnalysisResultWithTryJobInfo( 147 def _GetAnalysisResultWithTryJobInfo(show_debug_info, organized_results,
151 organized_results, master_name, builder_name, build_number): 148 master_name, builder_name, build_number):
152 """Reorganizes analysis result and try job result by step_name and culprit. 149 """Reorganizes analysis result and try job result by step_name and culprit.
153 150
154 Returns: 151 Returns:
155 update_result (dict): A dict of classified results. 152 update_result (dict): A dict of classified results.
156 153
157 The format for those dicts are as below: 154 The format for those dicts are as below:
158 { 155 {
159 # A dict of results that contains both 156 # A dict of results that contains both
160 # heuristic analysis results and try job results. 157 # heuristic analysis results and try job results.
161 'reliable_failures': { 158 'reliable_failures': {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 # such as non-swarming tests or swarming rerun failed. 195 # such as non-swarming tests or swarming rerun failed.
199 'unclassified_failures': {...} 196 'unclassified_failures': {...}
200 } 197 }
201 """ 198 """
202 updated_results = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) 199 updated_results = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
203 200
204 if not organized_results: 201 if not organized_results:
205 return updated_results 202 return updated_results
206 203
207 try_job_info = handlers_util.GetAllTryJobResults( 204 try_job_info = handlers_util.GetAllTryJobResults(
208 master_name, builder_name, build_number) 205 master_name, builder_name, build_number, show_debug_info)
206
209 if not try_job_info: 207 if not try_job_info:
210 return updated_results 208 return updated_results
211 209
212 for step_name, try_jobs in try_job_info.iteritems(): 210 for step_name, try_jobs in try_job_info.iteritems():
213 try_jobs = try_jobs['try_jobs'] 211 try_jobs = try_jobs['try_jobs']
214 step_heuristic_results = organized_results[step_name] 212 step_heuristic_results = organized_results[step_name]
215 step_updated_results = updated_results[step_name]['results'] 213 step_updated_results = updated_results[step_name]['results']
216 214
217 # Finds out try job result index and heuristic result index for each test. 215 # Finds out try job result index and heuristic result index for each test.
218 test_result_map = defaultdict(lambda: defaultdict(int)) 216 test_result_map = defaultdict(lambda: defaultdict(int))
(...skipping 27 matching lines...) Expand all
246 'try_job': try_job_result, 244 'try_job': try_job_result,
247 'heuristic_analysis': { 245 'heuristic_analysis': {
248 'suspected_cls': heuristic_result['suspected_cls'] 246 'suspected_cls': heuristic_result['suspected_cls']
249 }, 247 },
250 'tests': tests if tests != [NON_SWARMING] else [], 248 'tests': tests if tests != [NON_SWARMING] else [],
251 'first_failure': heuristic_result['first_failure'], 249 'first_failure': heuristic_result['first_failure'],
252 'last_pass': heuristic_result['last_pass'], 250 'last_pass': heuristic_result['last_pass'],
253 'supported': heuristic_result['supported'] 251 'supported': heuristic_result['supported']
254 } 252 }
255 253
256 if ('status' not in try_job_result or 254 if (('status' not in try_job_result or
257 try_job_result['status'] in NO_TRY_JOB_REASON_MAP.values()): 255 try_job_result['status'] in NO_TRY_JOB_REASON_MAP.values()) or
256 show_debug_info):
258 # There is no try job info but only heuristic result. 257 # There is no try job info but only heuristic result.
259 try_job_result['status'] = try_job_result.get( 258 try_job_result['status'] = try_job_result.get(
260 'status', result_status.UNKNOWN) 259 'status', result_status.UNKNOWN)
261 step_updated_results['unclassified_failures'].append(final_result) 260 step_updated_results['unclassified_failures'].append(final_result)
262 elif try_job_result['status'] == result_status.FLAKY: 261 elif try_job_result['status'] == result_status.FLAKY:
263 step_updated_results['flaky_failures'].append(final_result) 262 step_updated_results['flaky_failures'].append(final_result)
264 else: 263 else:
265 step_updated_results['reliable_failures'].append(final_result) 264 step_updated_results['reliable_failures'].append(final_result)
266 265
267 return updated_results 266 return updated_results
268 267
269 268
270 class BuildFailure(BaseHandler): 269 class BuildFailure(BaseHandler):
271 PERMISSION_LEVEL = Permission.ANYONE 270 PERMISSION_LEVEL = Permission.ANYONE
272 271
273 def _ShowDebugInfo(self): 272 def _ShowDebugInfo(self):
274 # Show debug info only if the app is run locally during development, if the 273 # Show debug info only if the app is run locally during development, if the
275 # currently logged-in user is an admin, or if it is explicitly requested 274 # currently logged-in user is an admin, or if it is explicitly requested
276 # with parameter 'debug=1'. 275 # with parameter 'debug=1'.
277 return ( 276 return users.is_current_user_admin() or self.request.get('debug') == '1'
278 users.is_current_user_admin() or self.request.get('debug') == '1')
279 277
280 def _ShowTriageHelpButton(self): 278 def _ShowTriageHelpButton(self):
281 return users.is_current_user_admin() 279 return users.is_current_user_admin()
282 280
283 def _PrepareCommonDataForFailure(self, analysis): 281 def _PrepareCommonDataForFailure(self, analysis):
284 return { 282 return {
285 'master_name': analysis.master_name, 283 'master_name': analysis.master_name,
286 'builder_name': analysis.builder_name, 284 'builder_name': analysis.builder_name,
287 'build_number': analysis.build_number, 285 'build_number': analysis.build_number,
288 'pipeline_status_path': analysis.pipeline_status_path, 286 'pipeline_status_path': analysis.pipeline_status_path,
(...skipping 18 matching lines...) Expand all
307 return try_job_data # pragma: no cover. 305 return try_job_data # pragma: no cover.
308 306
309 referred_build_keys = analysis.failure_result_map[ 307 referred_build_keys = analysis.failure_result_map[
310 constants.COMPILE_STEP_NAME].split('/') 308 constants.COMPILE_STEP_NAME].split('/')
311 try_job = WfTryJob.Get(*referred_build_keys) 309 try_job = WfTryJob.Get(*referred_build_keys)
312 if not try_job or not try_job.compile_results: 310 if not try_job or not try_job.compile_results:
313 return try_job_data # pragma: no cover. 311 return try_job_data # pragma: no cover.
314 result = try_job.compile_results[-1] 312 result = try_job.compile_results[-1]
315 313
316 try_job_data['status'] = analysis_status.STATUS_TO_DESCRIPTION.get( 314 try_job_data['status'] = analysis_status.STATUS_TO_DESCRIPTION.get(
317 try_job.status, 'unknown').lower() 315 try_job.status, 'unknown').lower()
318 try_job_data['url'] = result.get('url') 316 try_job_data['url'] = result.get('url')
319 try_job_data['completed'] = try_job.completed 317 try_job_data['completed'] = try_job.completed
320 try_job_data['failed'] = try_job.failed 318 try_job_data['failed'] = try_job.failed
321 try_job_data['culprit'] = result.get( 319 try_job_data['culprit'] = result.get(
322 'culprit', {}).get(constants.COMPILE_STEP_NAME) 320 'culprit', {}).get(constants.COMPILE_STEP_NAME)
323 321
324 return try_job_data 322 return try_job_data
325 323
326 @staticmethod 324 @staticmethod
327 def _PopulateHeuristicDataForCompileFailure(analysis, data): 325 def _PopulateHeuristicDataForCompileFailure(analysis, data):
(...skipping 10 matching lines...) Expand all
338 def _PrepareDataForCompileFailure(self, analysis): 336 def _PrepareDataForCompileFailure(self, analysis):
339 data = self._PrepareCommonDataForFailure(analysis) 337 data = self._PrepareCommonDataForFailure(analysis)
340 338
341 # Check result from heuristic analysis. 339 # Check result from heuristic analysis.
342 self._PopulateHeuristicDataForCompileFailure(analysis, data) 340 self._PopulateHeuristicDataForCompileFailure(analysis, data)
343 # Check result from try job. 341 # Check result from try job.
344 data['try_job'] = self._PrepareTryJobDataForCompileFailure(analysis) 342 data['try_job'] = self._PrepareTryJobDataForCompileFailure(analysis)
345 343
346 return data 344 return data
347 345
348 def _PrepareDataForTestFailures(self, analysis, build_info): 346 def _PrepareDataForTestFailures(self, analysis, build_info,
347 show_debug_info=False):
349 data = self._PrepareCommonDataForFailure(analysis) 348 data = self._PrepareCommonDataForFailure(analysis)
350 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP 349 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP
351 350
352 organized_results = _GetOrganizedAnalysisResultBySuspectedCL( 351 organized_results = _GetOrganizedAnalysisResultBySuspectedCL(
353 analysis.result) 352 analysis.result)
354 analysis_result = _GetAnalysisResultWithTryJobInfo( 353 analysis_result = _GetAnalysisResultWithTryJobInfo(
355 organized_results, *build_info) 354 show_debug_info, organized_results, *build_info)
355
356 data['analysis_result'] = analysis_result 356 data['analysis_result'] = analysis_result
357 357
358 return data 358 return data
359 359
360 def HandleGet(self): 360 def HandleGet(self):
361 """Triggers analysis of a build failure on demand and return current result. 361 """Triggers analysis of a build failure on demand and return current result.
362 362
363 If the final analysis result is available, set cache-control to 1 day to 363 If the final analysis result is available, set cache-control to 1 day to
364 avoid overload by unnecessary and frequent query from clients; otherwise 364 avoid overload by unnecessary and frequent query from clients; otherwise
365 set cache-control to 5 seconds to allow repeated query. 365 set cache-control to 5 seconds to allow repeated query.
(...skipping 15 matching lines...) Expand all
381 if not analysis: 381 if not analysis:
382 return BaseHandler.CreateError( 382 return BaseHandler.CreateError(
383 'Master "%s" is not supported yet.' % master_name, 501) 383 'Master "%s" is not supported yet.' % master_name, 501)
384 384
385 if not analysis: 385 if not analysis:
386 # Only allow admin to force a re-run and set the build_completed. 386 # Only allow admin to force a re-run and set the build_completed.
387 force = (users.is_current_user_admin() and 387 force = (users.is_current_user_admin() and
388 self.request.get('force') == '1') 388 self.request.get('force') == '1')
389 build_completed = (users.is_current_user_admin() and 389 build_completed = (users.is_current_user_admin() and
390 self.request.get('build_completed') == '1') 390 self.request.get('build_completed') == '1')
391 force_try_job = (users.is_current_user_admin() and
392 self.request.get('force_try_job') == '1')
391 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded( 393 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded(
392 master_name, builder_name, build_number, 394 master_name, builder_name, build_number,
393 build_completed=build_completed, force=force, 395 build_completed=build_completed, force=force,
396 force_try_job=force_try_job,
394 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) 397 queue_name=constants.WATERFALL_ANALYSIS_QUEUE)
395 398
396 if analysis.failure_type == failure_type.COMPILE: 399 if analysis.failure_type == failure_type.COMPILE:
397 return { 400 return {
398 'template': 'waterfall/compile_failure.html', 401 'template': 'waterfall/compile_failure.html',
399 'data': self._PrepareDataForCompileFailure(analysis), 402 'data': self._PrepareDataForCompileFailure(analysis),
400 } 403 }
401 else: 404 else:
402 return { 405 return {
403 'template': 'build_failure.html', 406 'template': 'build_failure.html',
404 'data': self._PrepareDataForTestFailures(analysis, build_info), 407 'data': self._PrepareDataForTestFailures(analysis, build_info,
408 self._ShowDebugInfo()),
405 } 409 }
406 410
407 def HandlePost(self): # pragma: no cover 411 def HandlePost(self): # pragma: no cover
408 return self.HandleGet() 412 return self.HandleGet()
OLDNEW
« no previous file with comments | « no previous file | appengine/findit/handlers/handlers_util.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698