Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 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 import logging | 5 import logging |
| 6 | 6 |
| 7 from common import appengine_util | 7 from common import appengine_util |
| 8 from common import constants | 8 from common import constants |
| 9 from common import time_util | 9 from common import time_util |
| 10 from model import analysis_status | 10 from model import analysis_status |
| 11 from model.flake.master_flake_analysis import MasterFlakeAnalysis | 11 from model.flake.master_flake_analysis import MasterFlakeAnalysis |
| 12 from waterfall import waterfall_config | 12 from waterfall import waterfall_config |
| 13 from waterfall.flake.recursive_flake_pipeline import RecursiveFlakePipeline | 13 from waterfall.flake.recursive_flake_pipeline import RecursiveFlakePipeline |
| 14 | 14 |
| 15 | 15 |
| 16 def _NeedANewAnalysis( | 16 def _NeedANewAnalysis( |
| 17 master_name, builder_name, build_number, step_name, test_name, | 17 normalized_test, original_test, algorithm_parameters, |
| 18 algorithm_parameters, allow_new_analysis=False, force=False): | 18 bug_id=None, allow_new_analysis=False, force=False): |
| 19 """Checks status of analysis for the test and decides if a new one is needed. | 19 """Checks status of analysis for the test and decides if a new one is needed. |
| 20 | 20 |
| 21 A MasterFlakeAnalysis entity for the given parameters will be created if none | 21 A MasterFlakeAnalysis entity for the given parameters will be created if none |
| 22 exists. When a new analysis is needed, this function will create and | 22 exists. When a new analysis is needed, this function will create and |
| 23 save a MasterFlakeAnalysis entity to the datastore. | 23 save a MasterFlakeAnalysis entity to the datastore. |
| 24 | 24 |
| 25 Args: | 25 Args: |
| 26 master_name (str): The master name on Waterfall. | 26 normalized_test (TestInfo): Info of the normalized flaky test after mapping |
| 27 builder_name (str): The builder name on Waterfall. | 27 a CQ trybot step to a Waterfall buildbot step, striping prefix "PRE_" |
| 28 build_number (int): The build number on Waterfall. | 28 from a gtest, etc. |
| 29 step_name (str): The step in which the flaky test is found. | 29 original_test (TestInfo): Info of the original flaky test. |
| 30 test_name (str): The flaky test to be analyzed. | 30 algorithm_parameters (dict): Algorithm parameters to run the analysis. |
| 31 bug_id (int): The monorail bug id to update when analysis is done. | |
| 31 allow_new_analysis (bool): Indicate whether a new analysis is allowed. | 32 allow_new_analysis (bool): Indicate whether a new analysis is allowed. |
| 32 force (bool): Indicate whether to force a rerun of current analysis. | 33 force (bool): Indicate whether to force a rerun of current analysis. |
| 33 | 34 |
| 34 Returns: | 35 Returns: |
| 35 (need_new_analysis, analysis) | 36 (need_new_analysis, analysis) |
| 36 need_new_analysis (bool): True if an analysis is needed, otherwise False. | 37 need_new_analysis (bool): True if an analysis is needed, otherwise False. |
| 37 analysis (MasterFlakeAnalysis): The MasterFlakeAnalysis entity. | 38 analysis (MasterFlakeAnalysis): The MasterFlakeAnalysis entity. |
| 38 """ | 39 """ |
| 39 analysis = MasterFlakeAnalysis.GetVersion( | 40 analysis = MasterFlakeAnalysis.GetVersion( |
| 40 master_name, builder_name, build_number, step_name, test_name) | 41 normalized_test.master_name, normalized_test.builder_name, |
| 42 normalized_test.build_number, normalized_test.step_name, | |
| 43 normalized_test.test_name) | |
| 44 | |
| 45 def PopulateInfoToAnalysis(analysis): | |
|
lijeffrey
2016/10/21 17:10:27
nit: How about rename this PopulateAnalysisInfo? A
stgao
2016/10/21 22:42:34
TestInfo is not a NDB model so it is not in model/
| |
| 46 analysis.Reset() | |
| 47 analysis.request_time = time_util.GetUTCNow() | |
| 48 analysis.status = analysis_status.PENDING | |
| 49 analysis.algorithm_parameters = algorithm_parameters | |
| 50 analysis.version = appengine_util.GetCurrentVersion() | |
| 51 analysis.original_master_name = original_test.master_name | |
| 52 analysis.original_builder_name = original_test.builder_name | |
| 53 analysis.original_build_number = original_test.build_number | |
| 54 analysis.original_step_name = original_test.step_name | |
| 55 analysis.original_test_name = original_test.test_name | |
| 56 analysis.bug_id = bug_id | |
| 41 | 57 |
| 42 if not analysis: | 58 if not analysis: |
| 43 if not allow_new_analysis: | 59 if not allow_new_analysis: |
| 44 return False, None | 60 return False, None |
| 45 analysis = MasterFlakeAnalysis.Create( | 61 analysis = MasterFlakeAnalysis.Create( |
| 46 master_name, builder_name, build_number, step_name, test_name) | 62 normalized_test.master_name, normalized_test.builder_name, |
| 47 analysis.request_time = time_util.GetUTCNow() | 63 normalized_test.build_number, normalized_test.step_name, |
| 48 analysis.status = analysis_status.PENDING | 64 normalized_test.test_name) |
| 49 analysis.algorithm_parameters = algorithm_parameters | 65 PopulateInfoToAnalysis(analysis) |
| 50 analysis.version = appengine_util.GetCurrentVersion() | |
| 51 _, saved = analysis.Save() | 66 _, saved = analysis.Save() |
| 52 return saved, analysis | 67 return saved, analysis |
| 53 elif (analysis.status == analysis_status.PENDING or | 68 elif (analysis.status == analysis_status.PENDING or |
| 54 analysis.status == analysis_status.RUNNING): | 69 analysis.status == analysis_status.RUNNING): |
| 55 return False, analysis | 70 return False, analysis |
| 56 elif allow_new_analysis and force and analysis.status in ( | 71 elif allow_new_analysis and force and analysis.status in ( |
| 57 analysis_status.ERROR, analysis_status.COMPLETED): | 72 analysis_status.ERROR, analysis_status.COMPLETED): |
| 58 analysis.Reset() | 73 PopulateInfoToAnalysis(analysis) |
| 59 analysis.request_time = time_util.GetUTCNow() | |
| 60 analysis.status = analysis_status.PENDING | |
| 61 analysis.algorithm_parameters = algorithm_parameters | |
| 62 analysis.version = appengine_util.GetCurrentVersion() | |
| 63 _, saved = analysis.Save() | 74 _, saved = analysis.Save() |
| 64 return saved, analysis | 75 return saved, analysis |
| 65 else: | 76 else: |
| 66 return False, analysis | 77 return False, analysis |
| 67 | 78 |
| 68 | 79 |
| 69 def ScheduleAnalysisIfNeeded(master_name, builder_name, build_number, step_name, | 80 def ScheduleAnalysisIfNeeded( |
| 70 test_name, allow_new_analysis=False, force=False, | 81 normalized_test, original_test, bug_id=None, |
| 71 manually_triggered=False, | 82 allow_new_analysis=False, force=False, manually_triggered=False, |
| 72 queue_name=constants.DEFAULT_QUEUE): | 83 queue_name=constants.DEFAULT_QUEUE): |
| 73 """Schedules an analysis if needed and returns the MasterFlakeAnalysis. | 84 """Schedules an analysis if needed and returns the MasterFlakeAnalysis. |
| 74 | 85 |
| 75 When the build failure was already analyzed and a new analysis is scheduled, | 86 When the build failure was already analyzed and a new analysis is scheduled, |
| 76 the returned WfAnalysis will still have the result of last completed analysis. | 87 the returned WfAnalysis will still have the result of last completed analysis. |
| 77 | 88 |
| 78 Args: | 89 Args: |
| 79 master_name (str): The master name of the failed test | 90 normalized_test (TestInfo): Info of the normalized flaky test after mapping |
| 80 builder_name (str): The builder name of the failed test | 91 a CQ trybot step to a Waterfall buildbot step, striping prefix "PRE_" |
| 81 build_number (int): The build number of the failed test | 92 from a gtest, etc. |
| 82 step_name (str): The name of the test suite | 93 original_test (TestInfo): Info of the original flaky test. |
| 83 test_name (str): The single test we are checking | 94 bug_id (int): The monorail bug id to update when analysis is done. |
| 84 allow_new_analysis (bool): Indicate whether a new analysis is allowed. | 95 allow_new_analysis (bool): Indicate whether a new analysis is allowed. |
| 85 force (bool): Indicate whether to force a rerun of current analysis. | 96 force (bool): Indicate whether to force a rerun of current analysis. |
| 86 manually_triggered (bool): True if the analysis is from manual request, like | 97 manually_triggered (bool): True if the analysis is from manual request, like |
| 87 by a Chromium sheriff. | 98 by a Chromium sheriff. |
| 88 queue_name (str): The App Engine queue to run the analysis. | 99 queue_name (str): The App Engine queue to run the analysis. |
| 89 | 100 |
| 90 Returns: | 101 Returns: |
| 91 A MasterFlakeAnalysis instance. | 102 A MasterFlakeAnalysis instance. |
| 92 None if no analysis was scheduled and the user has no permission to. | 103 None if no analysis was scheduled and the user has no permission to. |
| 93 """ | 104 """ |
| 94 algorithm_parameters = waterfall_config.GetCheckFlakeSettings() | 105 algorithm_parameters = waterfall_config.GetCheckFlakeSettings() |
| 95 | 106 |
| 96 need_new_analysis, analysis = _NeedANewAnalysis( | 107 need_new_analysis, analysis = _NeedANewAnalysis( |
| 97 master_name, builder_name, build_number, step_name, test_name, | 108 normalized_test, original_test, algorithm_parameters, bug_id=bug_id, |
| 98 algorithm_parameters, allow_new_analysis, force) | 109 allow_new_analysis=allow_new_analysis, force=force) |
| 99 | 110 |
| 100 if need_new_analysis: | 111 if need_new_analysis: |
| 101 # _NeedANewAnalysis just created master_flake_analysis. Use the latest | 112 # _NeedANewAnalysis just created master_flake_analysis. Use the latest |
| 102 # version number and pass that along to the other pipelines for updating | 113 # version number and pass that along to the other pipelines for updating |
| 103 # results and data. | 114 # results and data. |
| 104 logging.info( | 115 logging.info( |
| 105 'A new master flake analysis was successfully saved for %s/%s/%s/%s/%s ' | 116 'A new master flake analysis was successfully saved for %s (%s) and ' |
| 106 'and will be captured in version %s', master_name, builder_name, | 117 'will be captured in version %s', repr(normalized_test), |
| 107 build_number, step_name, test_name, analysis.version_number) | 118 repr(original_test), analysis.version_number) |
| 108 | 119 |
| 109 max_build_numbers_to_look_back = algorithm_parameters.get( | 120 max_build_numbers_to_look_back = algorithm_parameters.get( |
| 110 'max_build_numbers_to_look_back') | 121 'max_build_numbers_to_look_back') |
| 111 flakiness_algorithm_results_dict = { | 122 flakiness_algorithm_results_dict = { |
| 112 'flakes_in_a_row': 0, | 123 'flakes_in_a_row': 0, |
| 113 'stable_in_a_row': 0, | 124 'stable_in_a_row': 0, |
| 114 'stabled_out': False, | 125 'stabled_out': False, |
| 115 'flaked_out': False, | 126 'flaked_out': False, |
| 116 'last_build_number': max( | 127 'last_build_number': max( |
| 117 0, build_number - max_build_numbers_to_look_back), | 128 0, normalized_test.build_number - max_build_numbers_to_look_back), |
| 118 'lower_boundary': None, | 129 'lower_boundary': None, |
| 119 'upper_boundary': None, | 130 'upper_boundary': None, |
| 120 'lower_boundary_result': None, | 131 'lower_boundary_result': None, |
| 121 'sequential_run_index': 0 | 132 'sequential_run_index': 0 |
| 122 } | 133 } |
| 123 | 134 |
| 124 pipeline_job = RecursiveFlakePipeline( | 135 pipeline_job = RecursiveFlakePipeline( |
| 125 master_name, builder_name, build_number, step_name, test_name, | 136 normalized_test.master_name, normalized_test.builder_name, |
| 126 analysis.version_number, master_build_number=build_number, | 137 normalized_test.build_number, normalized_test.step_name, |
| 138 normalized_test.test_name, analysis.version_number, | |
| 139 master_build_number=normalized_test.build_number, | |
| 127 flakiness_algorithm_results_dict=flakiness_algorithm_results_dict, | 140 flakiness_algorithm_results_dict=flakiness_algorithm_results_dict, |
| 128 manually_triggered=manually_triggered) | 141 manually_triggered=manually_triggered) |
| 129 pipeline_job.target = appengine_util.GetTargetNameForModule( | 142 pipeline_job.target = appengine_util.GetTargetNameForModule( |
| 130 constants.WATERFALL_BACKEND) | 143 constants.WATERFALL_BACKEND) |
| 131 pipeline_job.StartOffPSTPeakHours(queue_name=queue_name) | 144 pipeline_job.StartOffPSTPeakHours(queue_name=queue_name) |
| 132 | 145 |
| 133 return analysis | 146 return analysis |
| OLD | NEW |