blob: 09cb6c00a8567d2121302dba6e99bb855a394981 [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 Hoodf9a36512013-06-13 18:39:56 -0700105def get_tests_to_analyze():
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 Hood1a3c8dd2013-05-29 17:41:50 -0700112 @return the dict of test_name:last_finish_time pairs.
113
114 """
Keyar Hoode7b11842013-06-25 15:41:59 -0700115 recent_test_names = get_recently_ran_test_names()
Keyar Hood0f26dba2013-07-18 17:49:32 -0700116 last_passes = utils.get_last_pass_times()
117 prepared_passes = prepare_last_passes(last_passes)
Keyar Hoode7b11842013-06-25 15:41:59 -0700118
119 running_passes = {}
Keyar Hood0f26dba2013-07-18 17:49:32 -0700120 for test, pass_time in prepared_passes.items():
Keyar Hoode7b11842013-06-25 15:41:59 -0700121 if test in recent_test_names:
122 running_passes[test] = pass_time
123
124 failures_names = recent_test_names.difference(running_passes)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700125 always_failed = {test: datetime.datetime.min for test in failures_names}
Keyar Hoode7b11842013-06-25 15:41:59 -0700126 return dict(always_failed.items() + running_passes.items())
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700127
128
Keyar Hood3d807682013-07-01 15:13:05 -0700129def store_results(tests, storage):
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700130 """
Keyar Hood3d807682013-07-01 15:13:05 -0700131 Store information about tests that have been failing for a long time.
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700132
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700133
134 @param tests: The test_name:time_of_last_pass pairs.
135 @param storage: The storage object.
136
137 """
138 failing_time_cutoff = datetime.timedelta(_DAYS_TO_BE_FAILING_TOO_LONG)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700139
140 today = datetime.datetime.today()
141 for test, last_fail in tests.iteritems():
142 if today - last_fail >= failing_time_cutoff:
143 if test not in storage:
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700144 storage[test] = today
145 else:
146 try:
147 del storage[test]
148 except KeyError:
149 pass
150
Keyar Hood3d807682013-07-01 15:13:05 -0700151
152def email_about_test_failure(storage):
153 """
154 Send an email about all the tests in the storage object if there are any.
155
156 @param storage: The storage object.
157
158 """
Keyar Hood4d5f9d12013-06-28 15:36:45 -0700159 if storage:
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700160 mail.send(_MAIL_RESULTS_FROM,
161 [_MAIL_RESULTS_TO],
162 [],
163 'Long Failing Tests',
164 'The following tests have been failing for '
165 'at least %s days:\n\n' % (_DAYS_TO_BE_FAILING_TOO_LONG) +
Keyar Hood4d5f9d12013-06-28 15:36:45 -0700166 '\n'.join(storage.keys()))
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700167
168
169def main():
170 """
171 The script code.
172
173 Allows other python code to import and run this code. This will be more
174 important if a nice way to test this code can be determined.
175
176 """
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700177 storage = load_storage()
Keyar Hoodf9a36512013-06-13 18:39:56 -0700178 tests = get_tests_to_analyze()
Keyar Hood3d807682013-07-01 15:13:05 -0700179 store_results(tests, storage)
180 email_about_test_failure(storage)
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700181 save_storage(storage)
182
183 return 0
184
185
186if __name__ == '__main__':
187 sys.exit(main())