Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: chrome/test/chromedriver/test/waterfall_builder_monitor.py

Issue 633213002: Waterfall builder monitoring script. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/test/chromedriver/test/waterfall_builder_config_sample.json ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Waterfall monitoring script.
7 This script checks all builders specified in the config file and sends
8 status email about any step failures in these builders. This also
9 reports a build as failure if the latest build on that builder was built
10 2 days back. (Number of days can be configured in the config file)
11
12 This script can be run as cronjob on a linux machine once a day and
13 get email notification for any waterfall specified in the config file.
14
15 Sample cronjob entry below. This entry will run the script everyday at 9 AM.
16 Include this in the crontab file.
17 0 9 * * * <Path to script> --config <Path to json file>
18 """
19
20 import datetime
21 import json
22 import optparse
23 import sys
24 import time
25 import traceback
26 import urllib
27
28 from datetime import timedelta
29 from email.mime.text import MIMEText
30 from subprocess import Popen, PIPE
31
32
33 SUCCESS_SUBJECT = ('[CHROME TESTING]: Builder status %s: PASSED.')
34 FAILURE_SUBJECT = ('[CHROME TESTING]: Builder status %s: FAILED %d out of %d')
35 EXCEPTION_SUBJECT = ('Exception occurred running waterfall_builder_monitor.py '
36 'script')
37
38
39 def GetTimeDelta(date, days):
40 if isinstance(date, datetime.datetime):
41 return date + timedelta(days)
42
43
44 def GetDateFromEphocFormat(ephoc_time):
45 last_build_date = time.localtime(ephoc_time)
46 last_build_date = datetime.datetime(int(last_build_date.tm_year),
47 int(last_build_date.tm_mon),
48 int(last_build_date.tm_mday),
49 int(last_build_date.tm_hour),
50 int(last_build_date.tm_min),
51 int(last_build_date.tm_sec))
52 return last_build_date
53
54
55 def GetJSONData(json_url):
56 response = urllib.urlopen(json_url)
57 if response.getcode() == 200:
58 try:
59 data = json.loads(response.read())
60 except ValueError:
61 print 'ValueError for JSON URL: %s' % json_url
62 raise
63 else:
64 raise Exception('Error from URL: %s' % json_url)
65 response.close()
66 return data
67
68
69 def SendEmailViaSendmailCommand(sender_email, recipient_emails,
70 subject, email_body):
71 msg = MIMEText(email_body)
72 msg["From"] = sender_email
73 msg["To"] = recipient_emails
74 msg["Subject"] = subject
75 pipe = Popen(["/usr/sbin/sendmail", "-t"], stdin=PIPE)
76 pipe.communicate(msg.as_string())
77
78
79 def SendStatusEmailViaSendmailCommand(consolidated_results,
80 recipient_emails,
81 sender_email):
82 count = 0
stgao 2014/10/13 19:49:02 Rename |count| to |failure_count|?
pshenoy 2014/10/13 19:57:38 Done.
83 for result in consolidated_results:
84 if result['error'] != 'passed':
stgao 2014/10/13 19:49:02 if result['error'] != 'passed' and not result['bui
pshenoy 2014/10/13 19:57:38 Done.
85 count += 1
86 today = str(datetime.date.today()).replace('-', '/')[5:]
87 if count == 0:
88 subject = SUCCESS_SUBJECT % today
89 else:
90 subject = FAILURE_SUBJECT % (today, count, len(consolidated_results))
91
92 email_body = ''
93 for result in consolidated_results:
94 if result['error'] != 'passed' or result['build_too_old']:
95 if result['build_date'] is not None:
96 email_body += result['platform'] + ': ' +\
97 result['build_link'] + ' ( Build too old: ' +\
98 result['build_date'] + ' ) ' +'\n\n'
99 else:
100 email_body += result['platform'] + ': ' +\
101 result['build_link'] + '\n\n'
102
103 SendEmailViaSendmailCommand(sender_email, recipient_emails,
104 subject, email_body)
105
106
107 def SendExceptionEmailViaSendmailCommand(exception_message_lines,
108 recipient_emails,
109 sender_email):
110 subject = EXCEPTION_SUBJECT
111 email_body = ''
112 email_body = '\n'.join(exception_message_lines)
113
114 SendEmailViaSendmailCommand(sender_email, recipient_emails,
115 subject, email_body)
116
117
118 class OfficialBuilderParser(object):
119 """This class implements basic utility functions on a specified builder."""
120 def __init__(self, builder_type, build_info):
121 self.platform = builder_type
122 self.builder_info = build_info
123 self.builder_url = build_info['builder_url']
124 self.build_json_url = build_info['json_url']
125 self.build = self._GetLatestBuildNumber()
126
127 def _GetLatestBuildNumber(self):
128 json_url = self.builder_info['builds_url']
129 data = GetJSONData(json_url)
130 # Get a sorted list of all the keys in the json data.
131 keys = sorted(data)
132 return self._GetLatestCompletedBuild(keys)
133
134 def _GetLatestCompletedBuild(self, keys):
135 reversed_list = keys[::-1]
136 for build in reversed_list:
137 data = self._GetJSONDataForBuild(build)
138 if data is not None:
139 if 'text' in data:
140 return build
141 return None
142
143 def _GetJSONDataForBuild(self, build):
144 if build is None:
145 return build
146 json_url = self.build_json_url % build
147 return GetJSONData(json_url)
148
149
150 class GetBuilderStatus(OfficialBuilderParser):
151 def __init__(self, builder_type, build_info):
152 OfficialBuilderParser.__init__(self, builder_type, build_info)
153
154 def CheckForFailedSteps(self, days):
155 if self.build is None:
156 return {}
157 result = {'platform': self.platform,
158 'build_number': self.build,
159 'build_link': self.builder_url + self.build,
160 'build_date': None,
161 'build_too_old': False,
162 'error': 'unknown'}
163 data = self._GetJSONDataForBuild(self.build)
164 if data is not None:
165 if 'text' in data:
166 if 'build' in data['text'] and 'successful' in data['text']:
167 result['error'] = 'passed'
168 else:
169 if 'failed' in data['text'] or\
170 'exception' in data['text'] or\
171 'interrupted' in data['text']:
172 result['error'] = 'failed'
173 if 'times' in data:
174 old_date = GetTimeDelta(datetime.datetime.now(), days)
175 last_build_date = GetDateFromEphocFormat(data['times'][0])
176 if last_build_date < old_date:
177 result['build_too_old'] = True
178 result['build_date'] = str(last_build_date).split(' ')[0]
179 else:
180 raise Exception('There was some problem getting JSON data '
181 'from URL: %s' % result['build_link'])
182 return result
183
184 def main():
185 parser = optparse.OptionParser()
186 parser.add_option('--config', type='str',
187 help='Absolute path to the config file.')
188
189 (options, _) = parser.parse_args()
190 if not options.config:
191 print 'Error: missing required parameter: --config'
192 parser.print_help()
193 return 1
194
195 try:
196 with open(options.config, 'r') as config_file:
197 try:
198 json_data = json.loads(config_file.read())
199 except ValueError:
200 print 'ValueError for loading JSON data from : %s' % options.config
201 raise ValueError
202
203 old_build_days = -2
204 if 'old_build_days' in json_data:
205 old_build_days = int('-' + str(json_data['old_build_days']))
stgao 2014/10/13 19:49:02 old_build_days = - json_data['old_build_days']
pshenoy 2014/10/13 19:57:38 Done.
206 consolidated_results = []
207 for key in json_data['build_info'].keys():
208 builder_status = GetBuilderStatus(key, json_data['build_info'][key])
209 builder_result = builder_status.CheckForFailedSteps(old_build_days)
210 consolidated_results.append(builder_result)
211
212 SendStatusEmailViaSendmailCommand(consolidated_results,
213 json_data['recipient_emails'],
214 json_data['sender_email'])
215 return 0
216 except Exception:
217 formatted_lines = traceback.format_exc().splitlines()
218 SendExceptionEmailViaSendmailCommand(formatted_lines,
219 json_data['recipient_emails'],
220 json_data['sender_email'])
221 return 1
222
223 if __name__ == '__main__':
224 sys.exit(main())
OLDNEW
« no previous file with comments | « chrome/test/chromedriver/test/waterfall_builder_config_sample.json ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698