blob: 5d6b8dcd60fef3cd6540240e4db6c6673d35fcd4 [file] [log] [blame]
Keyar Hood1a3c8dd2013-05-29 17:41:50 -07001#!/usr/bin/python
2#
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7
Keyar Hood2ff7b412013-08-20 11:48:06 -07008import argparse, datetime, sys
Keyar Hood1a3c8dd2013-05-29 17:41:50 -07009
10import common
Keyar Hoodf9a36512013-06-13 18:39:56 -070011from autotest_lib.client.common_lib import mail
12from autotest_lib.frontend import setup_django_readonly_environment
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070013
Keyar Hoodf9a36512013-06-13 18:39:56 -070014# Django and the models are only setup after
15# the setup_django_readonly_environment module is imported.
16from autotest_lib.frontend.tko import models as tko_models
Keyar Hood0f26dba2013-07-18 17:49:32 -070017from autotest_lib.frontend.health import utils
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070018
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070019
Keyar Hoode7b11842013-06-25 15:41:59 -070020# Mark a test as failing too long if it has not passed in this many days
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070021_DAYS_TO_BE_FAILING_TOO_LONG = 60
Keyar Hoode7b11842013-06-25 15:41:59 -070022# Ignore any tests that have not ran in this many days
23_DAYS_NOT_RUNNING_CUTOFF = 60
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070024_MAIL_RESULTS_FROM = 'chromeos-test-health@google.com'
25_MAIL_RESULTS_TO = 'chromeos-lab-infrastructure@google.com'
26
27
Keyar Hood005539d2013-06-24 14:27:57 -070028def is_valid_test_name(name):
29 """
30 Returns if a test name is valid or not.
31
32 There is a bunch of entries in the tko_test table that are not actually
33 test names. They are there as a side effect of how Autotest uses this
34 table.
35
36 Two examples of bad tests names are as follows:
37 link-release/R29-4228.0.0/faft_ec/firmware_ECPowerG3_SERVER_JOB
38 try_new_image-chormeos1-rack2-host2
39
40 @param name: The candidate test names to check.
41 @return True if name is a valid test name and false otherwise.
42
43 """
44 return not '/' in name and not name.startswith('try_new_image')
45
46
Keyar Hood0f26dba2013-07-18 17:49:32 -070047def prepare_last_passes(last_passes):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070048 """
Keyar Hood0f26dba2013-07-18 17:49:32 -070049 Fix up the last passes so they can be used by the system.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070050
Keyar Hood0f26dba2013-07-18 17:49:32 -070051 This filters out invalid test names and converts the test names to utf8
52 encoding.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070053
Keyar Hood0f26dba2013-07-18 17:49:32 -070054 @param last_passes: The dictionary of test_name:last_pass pairs.
55
56 @return: Valid entries in encoded as utf8 strings.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070057 """
Keyar Hood0f26dba2013-07-18 17:49:32 -070058 valid_test_names = filter(is_valid_test_name, last_passes)
Keyar Hood005539d2013-06-24 14:27:57 -070059 # The shelve module does not accept Unicode objects as keys but does
60 # accept utf-8 strings.
Keyar Hood0f26dba2013-07-18 17:49:32 -070061 return {name.encode('utf8'): last_passes[name]
Keyar Hood005539d2013-06-24 14:27:57 -070062 for name in valid_test_names}
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070063
64
Keyar Hoode7b11842013-06-25 15:41:59 -070065def get_recently_ran_test_names():
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070066 """
Keyar Hoode7b11842013-06-25 15:41:59 -070067 Get all the test names from the database that have been recently ran.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070068
Keyar Hoode7b11842013-06-25 15:41:59 -070069 @return a set of the recently ran tests.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070070
71 """
Keyar Hoode7b11842013-06-25 15:41:59 -070072 cutoff_delta = datetime.timedelta(_DAYS_NOT_RUNNING_CUTOFF)
73 cutoff_date = datetime.datetime.today() - cutoff_delta
74 results = tko_models.Test.objects.filter(
75 started_time__gte=cutoff_date).values('test').distinct()
Keyar Hood005539d2013-06-24 14:27:57 -070076 test_names = [test['test'] for test in results]
77 valid_test_names = filter(is_valid_test_name, test_names)
Keyar Hoode7b11842013-06-25 15:41:59 -070078 return {test.encode('utf8') for test in valid_test_names}
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070079
80
Keyar Hooda3564712013-07-29 17:15:50 -070081def get_tests_to_analyze(recent_test_names, last_pass_times):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070082 """
Keyar Hoode7b11842013-06-25 15:41:59 -070083 Get all the recently ran tests as well as the last time they have passed.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070084
85 The minimum datetime is given as last pass time for tests that have never
86 passed.
87
Keyar Hooda3564712013-07-29 17:15:50 -070088 @param recent_test_names: The set of the names of tests that have been
89 recently ran.
90 @param last_pass_times: The dictionary of test_name:last_pass_time pairs.
91
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070092 @return the dict of test_name:last_finish_time pairs.
93
94 """
Keyar Hooda3564712013-07-29 17:15:50 -070095 prepared_passes = prepare_last_passes(last_pass_times)
Keyar Hoode7b11842013-06-25 15:41:59 -070096
97 running_passes = {}
Keyar Hood0f26dba2013-07-18 17:49:32 -070098 for test, pass_time in prepared_passes.items():
Keyar Hoode7b11842013-06-25 15:41:59 -070099 if test in recent_test_names:
100 running_passes[test] = pass_time
101
102 failures_names = recent_test_names.difference(running_passes)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700103 always_failed = {test: datetime.datetime.min for test in failures_names}
Keyar Hoode7b11842013-06-25 15:41:59 -0700104 return dict(always_failed.items() + running_passes.items())
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700105
106
Keyar Hood708f1fb2013-08-12 14:32:39 -0700107def email_about_test_failure(failed_tests, all_tests):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700108 """
Keyar Hood708f1fb2013-08-12 14:32:39 -0700109 Send an email about all the tests that have failed if there are any.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700110
Keyar Hood708f1fb2013-08-12 14:32:39 -0700111 @param failed_tests: The list of failed tests. This will be sorted in this
112 function.
Keyar Hooda3564712013-07-29 17:15:50 -0700113 @param all_tests: All the names of tests that have been recently ran.
Keyar Hood3d807682013-07-01 15:13:05 -0700114
115 """
Keyar Hood708f1fb2013-08-12 14:32:39 -0700116 if failed_tests:
117 failed_tests.sort()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700118 mail.send(_MAIL_RESULTS_FROM,
119 [_MAIL_RESULTS_TO],
120 [],
121 'Long Failing Tests',
Keyar Hooda3564712013-07-29 17:15:50 -0700122 '%d/%d tests have been failing for at least %d days.\n'
123 'They are the following:\n\n%s'
Keyar Hood708f1fb2013-08-12 14:32:39 -0700124 % (len(failed_tests), len(all_tests),
Keyar Hooda3564712013-07-29 17:15:50 -0700125 _DAYS_TO_BE_FAILING_TOO_LONG,
Keyar Hood708f1fb2013-08-12 14:32:39 -0700126 '\n'.join(failed_tests)))
127
128
129def filter_out_good_tests(tests):
130 """
131 Remove all tests that have passed recently enough to be good.
132
133 @param tests: The tests to filter on.
134
135 @return: A list of tests that have not passed for a long time.
136
137 """
138 cutoff = (datetime.datetime.today() -
139 datetime.timedelta(_DAYS_TO_BE_FAILING_TOO_LONG))
140 return [name for name, last_pass in tests.items() if last_pass < cutoff]
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700141
142
Keyar Hood2ff7b412013-08-20 11:48:06 -0700143def parse_options(args):
144 """Parse the command line options."""
145
146 description = ('Collects information about which tests have been '
147 'failing for a long time and creates an email summarizing '
148 'the results.')
149 parser = argparse.ArgumentParser(description=description)
150 parser.parse_args(args)
151
152
153def main(args=None):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700154 """
155 The script code.
156
157 Allows other python code to import and run this code. This will be more
158 important if a nice way to test this code can be determined.
159
Keyar Hood2ff7b412013-08-20 11:48:06 -0700160 @param args: The command line arguments being passed in.
161
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700162 """
Keyar Hood2ff7b412013-08-20 11:48:06 -0700163 args = [] if args is None else args
164 parse_options(args)
Keyar Hooda3564712013-07-29 17:15:50 -0700165 all_test_names = get_recently_ran_test_names()
166 last_passes = utils.get_last_pass_times()
167 tests = get_tests_to_analyze(all_test_names, last_passes)
Keyar Hood708f1fb2013-08-12 14:32:39 -0700168 failures = filter_out_good_tests(tests)
169 email_about_test_failure(failures, all_test_names)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700170
171
Keyar Hood2ff7b412013-08-20 11:48:06 -0700172
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700173if __name__ == '__main__':
Keyar Hood33ca53b2013-08-21 16:04:01 -0700174 sys.exit(main(sys.argv[1:]))