summaryrefslogtreecommitdiff
path: root/cbuildbot/stages/report_stages.py
diff options
context:
space:
mode:
Diffstat (limited to 'cbuildbot/stages/report_stages.py')
-rw-r--r--cbuildbot/stages/report_stages.py79
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 ''))