Sometimes, we just want to have important data about the job
readily available, without having to open the web interface.

This patch adds important info to the regular status e-mails:

 * Number of tests executed
 * Success rate
 * Details about test failures

So people can decide whether they actually want to inspect
the results more carefully.

Changes from v2:
 * Make sure results from SERVER_JOB and CLIENT_JOB.* will not
   disturb the user test stats in case of a job failure

Changes from v1:
 * Refactored the original code, that repeated a lot of logic to
   generate the subject and the body of the status e-mail. By
   refactoring it becomes a lot easier to make changes on the
   e-mail body if needed.
 * Fixed a bug, where job failures were being treated incorrectly.
 * Made floats to be displayed only with 2 decimals.

Signed-off-by: Lucas Meneghel Rodrigues <[email protected]>
---
 scheduler/scheduler_models.py |  129 ++++++++++++++++++++++++++++++++++++-----
 1 files changed, 115 insertions(+), 14 deletions(-)

diff --git a/scheduler/scheduler_models.py b/scheduler/scheduler_models.py
index bafaf5f..afc6cce 100644
--- a/scheduler/scheduler_models.py
+++ b/scheduler/scheduler_models.py
@@ -591,14 +591,56 @@ class HostQueueEntry(DBObject):
             _drone_manager.unregister_pidfile(pidfile_id)
 
 
+    def _get_status_email_contents(self, status, summary=None, hostname=None):
+        """
+        Gather info for the status notification e-mails.
+
+        If needed, we could start using the Django templating engine to create
+        the subject and the e-mail body, but that doesn't seem necessary right
+        now.
+
+        @param status: Job status text. Mandatory.
+        @param summary: Job summary text. Optional.
+        @param hostname: A hostname for the job. Optional.
+
+        @return: Tuple (subject, body) for the notification e-mail.
+        """
+        job_stats = Job(id=self.job.id).get_execution_details()
+
+        subject = ('Autotest | Job ID: %s "%s" | Status: %s ' %
+                    (self.job.id, self.job.name, status))
+
+        if hostname is not None:
+            subject += '| Hostname: %s ' % hostname
+
+        if status not in ["1 Failed", "Failed"]:
+            subject += '| Success Rate: %.2f %%' % job_stats['success_rate']
+
+        body =  "Job ID: %s\n" % self.job.id
+        body += "Job name: %s\n" % self.job.name
+        if hostname is not None:
+            body += "Host: %s\n" % hostname
+        if summary is not None:
+            body += "Summary: %s\n" % summary
+        body += "Status: %s\n" % status
+        body += "Results interface URL: %s\n" % self._view_job_url()
+        body += "Execution time (HH:MM:SS): %s\n" % job_stats['execution_time']
+        if int(job_stats['total_executed']) > 0:
+            body += "User tests executed: %s\n" % job_stats['total_executed']
+            body += "User tests passed: %s\n" % job_stats['total_passed']
+            body += "User tests failed: %s\n" % job_stats['total_failed']
+            body += ("User tests success rate: %.2f %%\n" %
+                     job_stats['success_rate'])
+        if job_stats['failed_rows']:
+            body += "Failures:\n"
+            body += job_stats['failed_rows']
+
+        return subject, body
+
+
     def _email_on_status(self, status):
         hostname = self._get_hostname()
-
-        subject = 'Autotest: Job ID: %s "%s" Host: %s %s' % (
-                self.job.id, self.job.name, hostname, status)
-        body = "Job ID: %s\nJob Name: %s\nHost: %s\nStatus: %s\n%s\n" % (
-                self.job.id, self.job.name, hostname, status,
-                self._view_job_url())
+        subject, body = self._get_status_email_contents(status, None, hostname)
         email_manager.manager.send_email(self.job.email_list, subject, body)
 
 
@@ -606,24 +648,20 @@ class HostQueueEntry(DBObject):
         if not self.job.is_finished():
             return
 
-        summary_text = []
+        summary = []
         hosts_queue = HostQueueEntry.fetch('job_id = %s' % self.job.id)
         for queue_entry in hosts_queue:
-            summary_text.append("Host: %s Status: %s" %
+            summary.append("Host: %s Status: %s" %
                                 (queue_entry._get_hostname(),
                                  queue_entry.status))
 
-        summary_text = "\n".join(summary_text)
+        summary = "\n".join(summary)
         status_counts = models.Job.objects.get_status_counts(
                 [self.job.id])[self.job.id]
         status = ', '.join('%d %s' % (count, status) for status, count
                     in status_counts.iteritems())
 
-        subject = 'Autotest: Job ID: %s "%s" %s' % (
-                self.job.id, self.job.name, status)
-        body = "Job ID: %s\nJob Name: %s\nStatus: %s\n%s\nSummary:\n%s" % (
-                self.job.id, self.job.name, status,  self._view_job_url(),
-                summary_text)
+        subject, body = self._get_status_email_contents(status, summary, None)
         email_manager.manager.send_email(self.job.email_list, subject, body)
 
 
@@ -821,6 +859,69 @@ class Job(DBObject):
         return entries
 
 
+    def get_execution_details(self):
+        """
+        Get test execution details for this job.
+
+        @return: Dictionary with test execution details
+        """
+        stats = {}
+        rows = _db.execute("""
+                SELECT t.test, s.word, t.reason
+                FROM tko_tests AS t, tko_jobs AS j, tko_status AS s
+                WHERE t.job_idx = j.job_idx
+                AND s.status_idx = t.status
+                AND j.afe_job_id = %s
+                """ % self.id)
+        total_executed = len(rows)
+        failed_rows = [r for r in rows if not 'GOOD' in r]
+
+        job_failure_count = 0
+        for r in failed_rows:
+            test_name = r[0]
+            # Here we are looking for tests such as SERVER_JOB and CLIENT_JOB.*
+            # Those are autotest 'internal job' tests, so they should not be
+            # Counted when evaluating the test stats
+            if 'JOB' in test_name:
+                job_failure_count += 1
+
+        if job_failure_count > 0:
+            total_executed -= job_failure_count
+            total_failed = len(failed_rows) - job_failure_count
+        else:
+            total_executed -= 2
+            total_failed = len(failed_rows)
+
+        if total_executed > 0:
+            success_rate = 100 - ((total_failed / float(total_executed)) * 100)
+        else:
+            success_rate = 0
+
+        failed_str = '%-30s %-10s %-40s\n' % ("Test Name", "Status", "Reason")
+        for row in failed_rows:
+            failed_str += '%-30s %-10s %-40s\n' % row
+
+        stats['total_executed'] = total_executed
+        stats['total_failed'] = total_failed
+        stats['total_passed'] = total_executed - total_failed
+        stats['success_rate'] = success_rate
+        stats['failed_rows'] = failed_str
+
+        time_row = _db.execute("""
+                   SELECT started_time, finished_time
+                   FROM tko_jobs
+                   WHERE afe_job_id = %s
+                   """ % self.id)
+
+        t_begin, t_end = time_row[0]
+        delta = t_end - t_begin
+        minutes, seconds = divmod(delta.seconds, 60)
+        hours, minutes = divmod(minutes, 60)
+        stats['execution_time'] = "%02d:%02d:%02d" % (hours, minutes, seconds)
+
+        return stats
+
+
     def set_status(self, status, update_queues=False):
         self.update_field('status',status)
 
-- 
1.7.2.2

_______________________________________________
Autotest mailing list
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest

Reply via email to