You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
7.1 KiB
186 lines
7.1 KiB
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import collections
|
|
import logging
|
|
|
|
import common
|
|
|
|
from autotest_lib.client.common_lib import global_config
|
|
from autotest_lib.server import site_utils
|
|
from autotest_lib.server.cros.dynamic_suite import job_status
|
|
from autotest_lib.server.cros.dynamic_suite import reporting_utils
|
|
from autotest_lib.server.cros.dynamic_suite import tools
|
|
from autotest_lib.site_utils import gmail_lib
|
|
|
|
try:
|
|
from chromite.lib import metrics
|
|
except ImportError:
|
|
metrics = site_utils.metrics_mock
|
|
|
|
|
|
EMAIL_CREDS_FILE = global_config.global_config.get_config_value(
|
|
'NOTIFICATIONS', 'gmail_api_credentials_test_failure', default=None)
|
|
|
|
|
|
class TestBug(object):
|
|
"""
|
|
Wrap up all information needed to make an intelligent report about an
|
|
issue. Each TestBug has a search marker associated with it that can be
|
|
used to find similar reports.
|
|
"""
|
|
|
|
def __init__(self, build, chrome_version, suite, result):
|
|
"""
|
|
@param build: The build type, of the form <board>/<milestone>-<release>.
|
|
eg: x86-mario-release/R25-4321.0.0
|
|
@param chrome_version: The chrome version associated with the build.
|
|
eg: 28.0.1498.1
|
|
@param suite: The name of the suite that this test run is a part of.
|
|
@param result: The status of the job associated with this issue.
|
|
This contains the status, job id, test name, hostname
|
|
and reason for issue.
|
|
"""
|
|
self.build = build
|
|
self.chrome_version = chrome_version
|
|
self.suite = suite
|
|
self.name = tools.get_test_name(build, suite, result.test_name)
|
|
self.reason = result.reason
|
|
# The result_owner is used to find results and logs.
|
|
self.result_owner = result.owner
|
|
self.hostname = result.hostname
|
|
self.job_id = result.id
|
|
|
|
# Aborts, server/client job failures or a test failure without a
|
|
# reason field need lab attention. Lab bugs for the aborted case
|
|
# are disabled till crbug.com/188217 is resolved.
|
|
self.lab_error = job_status.is_for_infrastructure_fail(result)
|
|
|
|
# The owner is who the bug is assigned to.
|
|
self.owner = ''
|
|
self.cc = []
|
|
self.components = []
|
|
|
|
if result.is_warn():
|
|
self.labels = ['Test-Warning']
|
|
self.status = 'Warning'
|
|
else:
|
|
self.labels = []
|
|
self.status = 'Failure'
|
|
|
|
|
|
def title(self):
|
|
"""Combines information about this bug into a title string."""
|
|
return '[%s] %s %s on %s' % (self.suite, self.name,
|
|
self.status, self.build)
|
|
|
|
|
|
def summary(self):
|
|
"""Combines information about this bug into a summary string."""
|
|
|
|
links = self._get_links_for_failure()
|
|
template = ('This report is automatically generated to track the '
|
|
'following %(status)s:\n'
|
|
'Test: %(test)s.\n'
|
|
'Suite: %(suite)s.\n'
|
|
'Chrome Version: %(chrome_version)s.\n'
|
|
'Build: %(build)s.\n\nReason:\n%(reason)s.\n'
|
|
'build artifacts: %(build_artifacts)s.\n'
|
|
'results log: %(results_log)s.\n'
|
|
'status log: %(status_log)s.\n'
|
|
'job link: %(job)s.\n\n'
|
|
'You may want to check the test history: '
|
|
'%(test_history_url)s\n'
|
|
'You may also want to check the test retry dashboard in '
|
|
'case this is a flakey test: %(retry_url)s\n')
|
|
|
|
specifics = {
|
|
'status': self.status,
|
|
'test': self.name,
|
|
'suite': self.suite,
|
|
'build': self.build,
|
|
'chrome_version': self.chrome_version,
|
|
'reason': self.reason,
|
|
'build_artifacts': links.artifacts,
|
|
'results_log': links.results,
|
|
'status_log': links.status_log,
|
|
'job': links.job,
|
|
'test_history_url': links.test_history_url,
|
|
'retry_url': links.retry_url,
|
|
}
|
|
|
|
return template % specifics
|
|
|
|
|
|
# TO-DO(shuqianz) Fix the dedupe failing issue because reason contains
|
|
# special characters after
|
|
# https://bugs.chromium.org/p/monorail/issues/detail?id=806 being fixed.
|
|
def search_marker(self):
|
|
"""Return an Anchor that we can use to dedupe this exact bug."""
|
|
board = ''
|
|
try:
|
|
board = site_utils.ParseBuildName(self.build)[0]
|
|
except site_utils.ParseBuildNameException as e:
|
|
logging.error(str(e))
|
|
|
|
# Substitute the board name for a placeholder. We try both build and
|
|
# release board name variants.
|
|
reason = self.reason
|
|
if board:
|
|
for b in (board, board.replace('_', '-')):
|
|
reason = reason.replace(b, 'BOARD_PLACEHOLDER')
|
|
|
|
return "%s{%s,%s,%s}" % ('Test%s' % self.status, self.suite,
|
|
self.name, reason)
|
|
|
|
|
|
def _get_links_for_failure(self):
|
|
"""Returns a named tuple of links related to this failure."""
|
|
links = collections.namedtuple('links', ('results,'
|
|
'status_log,'
|
|
'artifacts,'
|
|
'job,'
|
|
'test_history_url,'
|
|
'retry_url'))
|
|
return links(reporting_utils.link_result_logs(
|
|
self.job_id, self.result_owner, self.hostname),
|
|
reporting_utils.link_status_log(
|
|
self.job_id, self.result_owner, self.hostname),
|
|
reporting_utils.link_build_artifacts(self.build),
|
|
reporting_utils.link_job(self.job_id),
|
|
reporting_utils.link_test_history(self.name),
|
|
reporting_utils.link_retry_url(self.name))
|
|
|
|
|
|
ReportResult = collections.namedtuple('ReportResult', ['bug_id', 'update_count'])
|
|
|
|
|
|
def send_email(bug, bug_template):
|
|
"""Send email to the owner and cc's to notify the TestBug.
|
|
|
|
@param bug: TestBug instance.
|
|
@param bug_template: A template dictionary specifying the default bug
|
|
filing options for failures in this suite.
|
|
"""
|
|
to_set = set(bug.cc) if bug.cc else set()
|
|
if bug.owner:
|
|
to_set.add(bug.owner)
|
|
if bug_template.get('cc'):
|
|
to_set = to_set.union(bug_template.get('cc'))
|
|
if bug_template.get('owner'):
|
|
to_set.add(bug_template.get('owner'))
|
|
recipients = ', '.join(to_set)
|
|
if not recipients:
|
|
logging.warning('No owner/cc found. Will skip sending a mail.')
|
|
return
|
|
success = False
|
|
try:
|
|
gmail_lib.send_email(
|
|
recipients, bug.title(), bug.summary(), retry=False,
|
|
creds_path=site_utils.get_creds_abspath(EMAIL_CREDS_FILE))
|
|
success = True
|
|
finally:
|
|
(metrics.Counter('chromeos/autotest/errors/send_bug_email')
|
|
.increment(fields={'success': success}))
|