blob: e7efcf13164c7677f4a04d3c5220c036f92df249 [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 Hood4d5f9d12013-06-28 15:36:45 -07008import datetime, shelve, 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
20_STORAGE_FILE = 'failure_storage'
Keyar Hoode7b11842013-06-25 15:41:59 -070021# Mark a test as failing too long if it has not passed in this many days
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070022_DAYS_TO_BE_FAILING_TOO_LONG = 60
Keyar Hoode7b11842013-06-25 15:41:59 -070023# Ignore any tests that have not ran in this many days
24_DAYS_NOT_RUNNING_CUTOFF = 60
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070025_MAIL_RESULTS_FROM = 'chromeos-test-health@google.com'
26_MAIL_RESULTS_TO = 'chromeos-lab-infrastructure@google.com'
27
28
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070029def load_storage():
30 """
31 Loads the storage object from disk.
32
33 This object keeps track of which tests we have already sent mail about so
34 we only send emails when the status of a test changes.
35
36 @return the storage object.
37
38 """
39 return shelve.open(_STORAGE_FILE)
40
41
42def save_storage(storage):
43 """
44 Saves the storage object to disk.
45
46 @param storage: The storage object to save to disk.
47
48 """
49 storage.close()
50
51
Keyar Hood005539d2013-06-24 14:27:57 -070052def is_valid_test_name(name):
53 """
54 Returns if a test name is valid or not.
55
56 There is a bunch of entries in the tko_test table that are not actually
57 test names. They are there as a side effect of how Autotest uses this
58 table.
59
60 Two examples of bad tests names are as follows:
61 link-release/R29-4228.0.0/faft_ec/firmware_ECPowerG3_SERVER_JOB
62 try_new_image-chormeos1-rack2-host2
63
64 @param name: The candidate test names to check.
65 @return True if name is a valid test name and false otherwise.
66
67 """
68 return not '/' in name and not name.startswith('try_new_image')
69
70
Keyar Hood0f26dba2013-07-18 17:49:32 -070071def prepare_last_passes(last_passes):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070072 """
Keyar Hood0f26dba2013-07-18 17:49:32 -070073 Fix up the last passes so they can be used by the system.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070074
Keyar Hood0f26dba2013-07-18 17:49:32 -070075 This filters out invalid test names and converts the test names to utf8
76 encoding.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070077
Keyar Hood0f26dba2013-07-18 17:49:32 -070078 @param last_passes: The dictionary of test_name:last_pass pairs.
79
80 @return: Valid entries in encoded as utf8 strings.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070081 """
Keyar Hood0f26dba2013-07-18 17:49:32 -070082 valid_test_names = filter(is_valid_test_name, last_passes)
Keyar Hood005539d2013-06-24 14:27:57 -070083 # The shelve module does not accept Unicode objects as keys but does
84 # accept utf-8 strings.
Keyar Hood0f26dba2013-07-18 17:49:32 -070085 return {name.encode('utf8'): last_passes[name]
Keyar Hood005539d2013-06-24 14:27:57 -070086 for name in valid_test_names}
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070087
88
Keyar Hoode7b11842013-06-25 15:41:59 -070089def get_recently_ran_test_names():
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070090 """
Keyar Hoode7b11842013-06-25 15:41:59 -070091 Get all the test names from the database that have been recently ran.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070092
Keyar Hoode7b11842013-06-25 15:41:59 -070093 @return a set of the recently ran tests.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070094
95 """
Keyar Hoode7b11842013-06-25 15:41:59 -070096 cutoff_delta = datetime.timedelta(_DAYS_NOT_RUNNING_CUTOFF)
97 cutoff_date = datetime.datetime.today() - cutoff_delta
98 results = tko_models.Test.objects.filter(
99 started_time__gte=cutoff_date).values('test').distinct()
Keyar Hood005539d2013-06-24 14:27:57 -0700100 test_names = [test['test'] for test in results]
101 valid_test_names = filter(is_valid_test_name, test_names)
Keyar Hoode7b11842013-06-25 15:41:59 -0700102 return {test.encode('utf8') for test in valid_test_names}
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700103
104
Keyar Hooda3564712013-07-29 17:15:50 -0700105def get_tests_to_analyze(recent_test_names, last_pass_times):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700106 """
Keyar Hoode7b11842013-06-25 15:41:59 -0700107 Get all the recently ran tests as well as the last time they have passed.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700108
109 The minimum datetime is given as last pass time for tests that have never
110 passed.
111
Keyar Hooda3564712013-07-29 17:15:50 -0700112 @param recent_test_names: The set of the names of tests that have been
113 recently ran.
114 @param last_pass_times: The dictionary of test_name:last_pass_time pairs.
115
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700116 @return the dict of test_name:last_finish_time pairs.
117
118 """
Keyar Hooda3564712013-07-29 17:15:50 -0700119 prepared_passes = prepare_last_passes(last_pass_times)
Keyar Hoode7b11842013-06-25 15:41:59 -0700120
121 running_passes = {}
Keyar Hood0f26dba2013-07-18 17:49:32 -0700122 for test, pass_time in prepared_passes.items():
Keyar Hoode7b11842013-06-25 15:41:59 -0700123 if test in recent_test_names:
124 running_passes[test] = pass_time
125
126 failures_names = recent_test_names.difference(running_passes)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700127 always_failed = {test: datetime.datetime.min for test in failures_names}
Keyar Hoode7b11842013-06-25 15:41:59 -0700128 return dict(always_failed.items() + running_passes.items())
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700129
130
Keyar Hood3d807682013-07-01 15:13:05 -0700131def store_results(tests, storage):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700132 """
Keyar Hood3d807682013-07-01 15:13:05 -0700133 Store information about tests that have been failing for a long time.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700134
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700135
136 @param tests: The test_name:time_of_last_pass pairs.
137 @param storage: The storage object.
138
139 """
140 failing_time_cutoff = datetime.timedelta(_DAYS_TO_BE_FAILING_TOO_LONG)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700141
142 today = datetime.datetime.today()
143 for test, last_fail in tests.iteritems():
144 if today - last_fail >= failing_time_cutoff:
145 if test not in storage:
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700146 storage[test] = today
147 else:
148 try:
149 del storage[test]
150 except KeyError:
151 pass
152
Keyar Hood3d807682013-07-01 15:13:05 -0700153
Keyar Hooda3564712013-07-29 17:15:50 -0700154def email_about_test_failure(storage, all_tests):
Keyar Hood3d807682013-07-01 15:13:05 -0700155 """
156 Send an email about all the tests in the storage object if there are any.
157
158 @param storage: The storage object.
Keyar Hooda3564712013-07-29 17:15:50 -0700159 @param all_tests: All the names of tests that have been recently ran.
Keyar Hood3d807682013-07-01 15:13:05 -0700160
161 """
Keyar Hood4d5f9d12013-06-28 15:36:45 -0700162 if storage:
Keyar Hooda3564712013-07-29 17:15:50 -0700163 tests = sorted(storage.keys())
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700164 mail.send(_MAIL_RESULTS_FROM,
165 [_MAIL_RESULTS_TO],
166 [],
167 'Long Failing Tests',
Keyar Hooda3564712013-07-29 17:15:50 -0700168 '%d/%d tests have been failing for at least %d days.\n'
169 'They are the following:\n\n%s'
170 % (len(storage), len(all_tests),
171 _DAYS_TO_BE_FAILING_TOO_LONG,
172 '\n'.join(tests)))
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700173
174
175def main():
176 """
177 The script code.
178
179 Allows other python code to import and run this code. This will be more
180 important if a nice way to test this code can be determined.
181
182 """
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700183 storage = load_storage()
Keyar Hooda3564712013-07-29 17:15:50 -0700184 all_test_names = get_recently_ran_test_names()
185 last_passes = utils.get_last_pass_times()
186 tests = get_tests_to_analyze(all_test_names, last_passes)
Keyar Hood3d807682013-07-01 15:13:05 -0700187 store_results(tests, storage)
Keyar Hooda3564712013-07-29 17:15:50 -0700188 email_about_test_failure(storage, all_test_names)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700189 save_storage(storage)
190
191 return 0
192
193
194if __name__ == '__main__':
195 sys.exit(main())