diff options
Diffstat (limited to 'cbuildbot/stages/report_stages.py')
-rw-r--r-- | cbuildbot/stages/report_stages.py | 79 |
1 files changed, 64 insertions, 15 deletions
diff --git a/cbuildbot/stages/report_stages.py b/cbuildbot/stages/report_stages.py index c89a38fde..badf6c2b2 100644 --- a/cbuildbot/stages/report_stages.py +++ b/cbuildbot/stages/report_stages.py @@ -47,7 +47,7 @@ def WriteBasicMetadata(builder_run): In particular, this method does not write any metadata values that depend on the builder config, as the config may be modified by patches that are - applied before the final reexectuion. + applied before the final reexectuion. (exception: the config's name itself) This method is safe to run more than once (for instance, once per cbuildbot execution) because it will write the same data each time. @@ -135,6 +135,15 @@ class BuildStartStage(generic_stages.BuilderStage): self._run.config['doc']) WriteBasicMetadata(self._run) + + # This is a heuristic value for |important|, since patches that get applied + # later in the build might change the config. We write it now anyway, + # because in case the build fails before Sync, it is better to have this + # heuristic value than None. In BuildReexectuionFinishedStage, we re-write + # the definitive value. + self._run.attrs.metadata.UpdateWithDict( + {'important': self._run.config['important']}) + d = self._run.attrs.metadata.GetDict() # BuildStartStage should only run once per build. But just in case it @@ -165,7 +174,8 @@ class BuildStartStage(generic_stages.BuilderStage): build_config=d['bot-config'], bot_hostname=d['bot-hostname'], master_build_id=d['master_build_id'], - timeout_seconds=self._GetBuildTimeoutSeconds()) + timeout_seconds=self._GetBuildTimeoutSeconds(), + important=d['important']) self._run.attrs.metadata.UpdateWithDict({'build_id': build_id, 'db_type': db_type}) logging.info('Inserted build_id %s into cidb database type %s.', @@ -202,6 +212,51 @@ class BuildStartStage(generic_stages.BuilderStage): '%s.' % (metadata_dict['db_type'], db_type)) +class SlaveFailureSummaryStage(generic_stages.BuilderStage): + """Stage which summarizes and links to the failures of slave builds.""" + + @failures_lib.SetFailureType(failures_lib.InfrastructureFailure) + def PerformStage(self): + if not self._run.config.master: + logging.info('This stage is only meaningful for master builds. ' + 'Doing nothing.') + return + + build_id, db = self._run.GetCIDBHandle() + + if not db: + logging.info('No cidb connection for this build. ' + 'Doing nothing.') + return + + slave_failures = db.GetSlaveFailures(build_id) + failures_by_build = cros_build_lib.GroupByKey(slave_failures, 'build_id') + for build_id, build_failures in sorted(failures_by_build.items()): + failures_by_stage = cros_build_lib.GroupByKey(build_failures, + 'build_stage_id') + # Surface a link to each slave stage that failed, in stage_id sorted + # order. + for stage_id in sorted(failures_by_stage): + failure = failures_by_stage[stage_id][0] + # Ignore failures that did not cause their enclosing stage to fail. + # Ignore slave builds that are still inflight, because some stage logs + # might not have been printed to buildbot yet. + # TODO(akeshet) revisit this approach, if we seem to be suppressing + # useful information as a result of it. + if (failure['stage_status'] != constants.BUILDER_STATUS_FAILED or + failure['build_status'] == constants.BUILDER_STATUS_INFLIGHT): + continue + waterfall_url = constants.WATERFALL_TO_DASHBOARD[failure['waterfall']] + slave_stage_url = tree_status.ConstructDashboardURL( + waterfall_url, + failure['builder_name'], + failure['build_number'], + failure['stage_name']) + logging.PrintBuildbotLink('%s %s' % (failure['build_config'], + failure['stage_name']), + slave_stage_url) + + class BuildReexecutionFinishedStage(generic_stages.BuilderStage, generic_stages.ArchivingStageMixin): """The first stage to run after the final cbuildbot reexecution. @@ -271,6 +326,7 @@ class BuildReexecutionFinishedStage(generic_stages.BuilderStage, 'boards': config['boards'], 'child-configs': child_configs, 'build_type': config['build_type'], + 'important': config['important'], # Data for the toolchain used. 'sdk-version': sdk_verinfo.get('SDK_LATEST_VERSION', '<unknown>'), @@ -438,17 +494,6 @@ class ReportStage(generic_stages.BuilderStage, tree_status.SendHealthAlert(self._run, title, '\n\n'.join(body), extra_fields=extra_fields) - def _UploadMetadataForRun(self, final_status): - """Upload metadata.json for this entire run. - - Args: - final_status: Final status string for this run. - """ - self._run.attrs.metadata.UpdateWithDict( - self.GetReportMetadata(final_status=final_status, - completion_instance=self._completion_instance)) - self.UploadMetadata() - def _UploadArchiveIndex(self, builder_run): """Upload an HTML index for the artifacts at remote archive location. @@ -559,7 +604,7 @@ class ReportStage(generic_stages.BuilderStage, # Upload metadata, and update the pass/fail streak counter for the main # run only. These aren't needed for the child builder runs. - self._UploadMetadataForRun(final_status) + self.UploadMetadata() self._UpdateRunStreak(self._run, final_status) # Alert if the Pre-CQ has infra failures. @@ -615,6 +660,11 @@ class ReportStage(generic_stages.BuilderStage, # ArchiveResults() depends the existence of this attr. self._run.attrs.release_tag = None + # Set up our report metadata. + self._run.attrs.metadata.UpdateWithDict( + self.GetReportMetadata(final_status=final_status, + completion_instance=self._completion_instance)) + # Some operations can only be performed if a valid version is available. try: self._run.GetVersionInfo() @@ -626,7 +676,6 @@ class ReportStage(generic_stages.BuilderStage, archive_urls = '' metadata_url = '' - results_lib.Results.Report( sys.stdout, archive_urls=archive_urls, current_version=(self._run.attrs.release_tag or '')) |