[autotest] Include bugs filed in run_suite output.

When tests run through a suite fail, we attempt to
file bugs. This change records the bug id in a keyval
which is then parsed by run suite to log a link to the
bug filed.

TEST=ran a failing suite and noted the links generated.
BUG=chromium:217209

Change-Id: If8b48cb16ad8f1fb3c71283266c14299f4521770
Reviewed-on: https://gerrit.chromium.org/gerrit/49472
Tested-by: Prashanth Balasubramanian <beeps@chromium.org>
Commit-Queue: Prashanth Balasubramanian <beeps@chromium.org>
Reviewed-by: Prashanth Balasubramanian <beeps@chromium.org>
diff --git a/server/cros/dynamic_suite/constants.py b/server/cros/dynamic_suite/constants.py
index b1e94c1..c980210 100644
--- a/server/cros/dynamic_suite/constants.py
+++ b/server/cros/dynamic_suite/constants.py
@@ -13,6 +13,7 @@
 JOB_REPO_URL = 'job_repo_url'
 VERSION_PREFIX = 'cros-version:'
 BOARD_PREFIX = 'board:'
+BUG_KEYVAL = '-Bug_Id'
 
 # Timings
 ARTIFACT_FINISHED_TIME = 'artifact_finished_time'
diff --git a/server/cros/dynamic_suite/reporting.py b/server/cros/dynamic_suite/reporting.py
index c34a5b0..f953bcc 100644
--- a/server/cros/dynamic_suite/reporting.py
+++ b/server/cros/dynamic_suite/reporting.py
@@ -344,6 +344,7 @@
         @param title: Title of the bug.
         @param name: Failing Test name, used to assigning labels.
         @param owner: The owner of the new bug.
+        @return: id of the created issue.
         """
         issue_options = self._resolve_slotvals(
             bug_template, title=title,
@@ -360,6 +361,7 @@
         # with an owner. crbug.com/221757.
         if owner:
             self._modify_bug_report(issue.id, owner=owner)
+        return issue.id
 
 
     def _modify_bug_report(self, issue_id, comment='', owner=''):
@@ -484,10 +486,12 @@
         @param failure A TestFailure instance about the failure.
         @param bug_template: A template dictionary specifying the default bug
                              filing options for failures in this suite.
+
+        @return: The issue id of the issue that was either created or modified.
         """
         if not self._check_tracker():
             logging.error("Can't file %s", failure.bug_title())
-            return
+            return None
 
         issue = self._find_issue_by_marker(failure.search_marker())
         summary = '%s\n\n%s%s\n' % (failure.bug_summary(),
@@ -497,13 +501,13 @@
         if issue:
             comment = '%s\n\n%s' % (failure.bug_title(), summary)
             self._modify_bug_report(issue.id, comment)
-            return
+            return issue.id
 
         if failure.lab_error:
             if bug_template.get('labels'):
                 self._LAB_ERROR_TEMPLATE['labels'] += bug_template.get('labels')
             bug_template = self._LAB_ERROR_TEMPLATE
 
-        self._create_bug_report(summary, failure.bug_title(),
-                                failure.test, self._get_owner(failure),
-                                failure.get_milestone(), bug_template)
+        return self._create_bug_report(summary, failure.bug_title(),
+                                       failure.test, self._get_owner(failure),
+                                       failure.get_milestone(), bug_template)
diff --git a/server/cros/dynamic_suite/suite.py b/server/cros/dynamic_suite/suite.py
index 2681846..256cb0a 100644
--- a/server/cros/dynamic_suite/suite.py
+++ b/server/cros/dynamic_suite/suite.py
@@ -398,7 +398,24 @@
                                                     self._tag,
                                                     result)
 
-                    bug_reporter.report(failure, bug_template)
+                    bug_id = bug_reporter.report(failure, bug_template)
+                    try:
+                        # Attempting to use the name of a job with special
+                        # characters as a keyval will throw a ValueError. One
+                        # such case is with aborted jobs. Luckily, we don't
+                        # really care about the name, since the same name we
+                        # have here is inserted into the results database we can
+                        # use it as a key to retrieve the bug id. An example key
+                        # for an aborted job after replacing the '/' with '_':
+                        # lumpy-release_R28-3947.0.0_dummy_experimental_dummy_\
+                        # Pass-Bug_Id=xxxx, where xxxx is the id of the bug.
+                        utils.write_keyval(self._results_dir, {
+                            (result.test_name.replace('/', '_')+
+                             constants.BUG_KEYVAL): bug_id})
+                    except ValueError:
+                        logging.error('Unable to log keyval for test:%s '
+                                      'bugid: %s', result.test_name, bug_id)
+
         except Exception:  # pylint: disable=W0703
             logging.error(traceback.format_exc())
             Status('FAIL', self._tag,