blob: fffad8d6587c4edf509b97a18af2c175ad395cf3 [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
8import datetime, logging, shelve, sys
9
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
17from django.db import models as django_models
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070018
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070019
20_STORAGE_FILE = 'failure_storage'
21_DAYS_TO_BE_FAILING_TOO_LONG = 60
22_TEST_PASS_STATUS_INDEX = 6
23_MAIL_RESULTS_FROM = 'chromeos-test-health@google.com'
24_MAIL_RESULTS_TO = 'chromeos-lab-infrastructure@google.com'
25
26
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070027def load_storage():
28 """
29 Loads the storage object from disk.
30
31 This object keeps track of which tests we have already sent mail about so
32 we only send emails when the status of a test changes.
33
34 @return the storage object.
35
36 """
37 return shelve.open(_STORAGE_FILE)
38
39
40def save_storage(storage):
41 """
42 Saves the storage object to disk.
43
44 @param storage: The storage object to save to disk.
45
46 """
47 storage.close()
48
49
Keyar Hoodf9a36512013-06-13 18:39:56 -070050def get_last_pass_times():
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070051 """
52 Get all the tests that have passed and the time they last passed.
53
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070054 @return the dict of test_name:last_finish_time pairs for tests that have
55 passed.
56
57 """
Keyar Hoodf9a36512013-06-13 18:39:56 -070058 results = tko_models.Test.objects.values('test').filter(
59 status=_TEST_PASS_STATUS_INDEX).annotate(
60 last_pass=django_models.Max('started_time'))
61 # The shelve module does not accept Unicode objects as keys but utf-8
62 # strings are.
63 return {result['test'].encode('utf8'): result['last_pass']
64 for result in results}
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070065
66
Keyar Hoodf9a36512013-06-13 18:39:56 -070067def get_all_test_names():
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070068 """
69 Get all the test names from the database.
70
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070071 @return a list of all the test names.
72
73 """
Keyar Hoodf9a36512013-06-13 18:39:56 -070074 test_names = tko_models.Test.objects.values('test').distinct()
75 return [test['test'].encode('utf8') for test in test_names]
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070076
77
Keyar Hoodf9a36512013-06-13 18:39:56 -070078def get_tests_to_analyze():
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070079 """
80 Get all the tests as well as the last time they have passed.
81
82 The minimum datetime is given as last pass time for tests that have never
83 passed.
84
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070085 @return the dict of test_name:last_finish_time pairs.
86
87 """
Keyar Hoodf9a36512013-06-13 18:39:56 -070088 last_passes = get_last_pass_times()
89 all_test_names = get_all_test_names()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -070090 failures_names = (set(all_test_names) - set(last_passes.keys()))
91 always_failed = {test: datetime.datetime.min for test in failures_names}
92 return dict(always_failed.items() + last_passes.items())
93
94
95def email_about_test_failure(tests, storage):
96 """
97 Send emails based on the last time tests has passed.
98
99 This involves updating the storage and sending an email if a test has
100 failed for a long time and we have not already sent an email about that
101 test.
102
103 @param tests: The test_name:time_of_last_pass pairs.
104 @param storage: The storage object.
105
106 """
107 failing_time_cutoff = datetime.timedelta(_DAYS_TO_BE_FAILING_TOO_LONG)
108 update_status = []
109
110 today = datetime.datetime.today()
111 for test, last_fail in tests.iteritems():
112 if today - last_fail >= failing_time_cutoff:
113 if test not in storage:
114 update_status.append(test)
115 storage[test] = today
116 else:
117 try:
118 del storage[test]
119 except KeyError:
120 pass
121
122 if update_status:
123 logging.info('Found %i new failing tests out %i, sending email.',
124 len(update_status),
125 len(tests))
126 mail.send(_MAIL_RESULTS_FROM,
127 [_MAIL_RESULTS_TO],
128 [],
129 'Long Failing Tests',
130 'The following tests have been failing for '
131 'at least %s days:\n\n' % (_DAYS_TO_BE_FAILING_TOO_LONG) +
132 '\n'.join(update_status))
133
134
135def main():
136 """
137 The script code.
138
139 Allows other python code to import and run this code. This will be more
140 important if a nice way to test this code can be determined.
141
142 """
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700143 storage = load_storage()
Keyar Hoodf9a36512013-06-13 18:39:56 -0700144 tests = get_tests_to_analyze()
Keyar Hood1a3c8dd2013-05-29 17:41:50 -0700145 email_about_test_failure(tests, storage)
146 save_storage(storage)
147
148 return 0
149
150
151if __name__ == '__main__':
152 sys.exit(main())