OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2015 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 """Python utility that triggers and waits for tasks to complete on CTFE.""" | |
7 | |
8 import base64 | |
9 import hashlib | |
10 import json | |
11 import optparse | |
12 import requests | |
13 import sys | |
14 import time | |
15 | |
16 | |
17 CTFE_HOST = "http://ct.skia.org" | |
18 CTFE_QUEUE = CTFE_HOST + '/queue/' | |
19 CHROMIUM_PERF_TASK_POST_URI = CTFE_HOST + "/_/webhook_add_chromium_perf_task" | |
20 GET_CHROMIUM_PERF_RUN_STATUS_URI = CTFE_HOST + "/get_chromium_perf_run_status" | |
21 CHROMIUM_PERF_RUNS_HISTORY = CTFE_HOST + "/chromium_perf_runs/" | |
22 GCE_WEBHOOK_SALT_METADATA_URI = ( | |
23 "http://metadata/computeMetadata/v1/project/attributes/" | |
24 "webhook_request_salt") | |
25 | |
26 | |
27 POLLING_FREQUENCY_SECS = 60 # 1 minute. | |
28 TRYBOT_DEADLINE_SECS = 24 * 60 * 60 # 24 hours. | |
29 | |
30 | |
31 class CtTrybotException(Exception): | |
32 pass | |
33 | |
34 | |
35 def _CreateTaskJSON(options): | |
36 """Creates a JSON representation of the requested task.""" | |
37 task_params = {} | |
38 task_params["username"] = options.requester | |
39 task_params["benchmark"] = options.benchmark | |
40 task_params["platform"] = "Linux" | |
41 task_params["page_sets"] = "10k" | |
borenet
2015/09/24 18:42:46
Can we plumb the platform and page_sets parameters
rmistry
2015/09/24 18:51:18
Yes we could plumb different combinations of the p
| |
42 task_params["repeat_runs"] = "3" | |
43 task_params["benchmark_args"] = "--output-format=csv-pivot-table" | |
44 task_params["browser_args_nopatch"] = ( | |
45 "--disable-setuid-sandbox --enable-threaded-compositing " | |
46 "--enable-impl-side-painting") | |
47 task_params["browser_args_withpatch"] = ( | |
48 "--disable-setuid-sandbox --enable-threaded-compositing " | |
49 "--enable-impl-side-painting") | |
50 | |
51 trybot_params = {} | |
52 trybot_params["issue"] = options.issue | |
53 trybot_params["task"] = task_params | |
54 return json.dumps(trybot_params) | |
55 | |
56 | |
57 def _GetWebhookSaltFromMetadata(): | |
58 """Gets webhook_request_salt from GCE's metadata server.""" | |
59 headers = {"Metadata-Flavor": "Google"} | |
60 resp = requests.get(GCE_WEBHOOK_SALT_METADATA_URI, headers=headers) | |
61 if resp.status_code != 200: | |
62 raise CtTrybotException( | |
63 'Return code from %s was %s' % (GCE_WEBHOOK_SALT_METADATA_URI, | |
64 resp.status_code)) | |
65 return resp.text | |
66 | |
67 | |
68 def _TriggerTask(options): | |
69 """Triggers the requested task on CTFE and returns the new task's ID.""" | |
70 task = _CreateTaskJSON(options) | |
71 m = hashlib.sha512() | |
72 m.update(task) | |
73 m.update('notverysecret' if options.local else _GetWebhookSaltFromMetadata()) | |
74 encoded = base64.standard_b64encode(m.digest()) | |
75 | |
76 headers = { | |
77 "Content-type": "application/x-www-form-urlencoded", | |
78 "Accept": "application/json", | |
79 "X-Webhook-Auth-Hash": encoded} | |
80 resp = requests.post(CHROMIUM_PERF_TASK_POST_URI, task, headers=headers) | |
81 | |
82 if resp.status_code != 200: | |
83 raise CtTrybotException( | |
84 'Return code from %s was %s' % (CHROMIUM_PERF_TASK_POST_URI, | |
85 resp.status_code)) | |
86 try: | |
87 ret = json.loads(resp.text) | |
88 except ValueError, e: | |
89 raise CtTrybotException( | |
90 'Did not get a JSON response from %s: %s' % ( | |
91 CHROMIUM_PERF_TASK_POST_URI, e)) | |
92 return ret["taskID"] | |
93 | |
94 | |
95 def TriggerAndWait(options): | |
96 task_id = _TriggerTask(options) | |
97 | |
98 print | |
99 print 'Task %s has been successfull scheduled on CTFE (%s).' % ( | |
100 task_id, CHROMIUM_PERF_RUNS_HISTORY) | |
101 print 'You will get an email once the task has been picked up by the server.' | |
102 print | |
103 print | |
104 | |
105 # Now poll CTFE till the task completes or till deadline is hit. | |
106 time_started_polling = time.time() | |
107 while True: | |
108 if (time.time() - time_started_polling) > TRYBOT_DEADLINE_SECS: | |
109 raise CtTrybotException( | |
110 'Task did not complete in the deadline of %s seconds.' % ( | |
111 TRYBOT_DEADLINE_SECS)) | |
112 | |
113 # Get the status of the task the trybot added. | |
114 get_url = '%s?task_id=%s' % (GET_CHROMIUM_PERF_RUN_STATUS_URI, task_id) | |
115 resp = requests.get(get_url) | |
116 if resp.status_code != 200: | |
117 raise CtTrybotException( | |
118 'Return code from %s was %s' % (GET_CHROMIUM_PERF_RUN_STATUS_URI, | |
119 resp.status_code)) | |
120 try: | |
121 ret = json.loads(resp.text) | |
122 except ValueError, e: | |
123 raise CtTrybotException( | |
124 'Did not get a JSON response from %s: %s' % (get_url, e)) | |
125 # Assert that the status is for the task we asked for. | |
126 assert int(ret["taskID"]) == int(task_id) | |
127 | |
128 status = ret["status"] | |
129 if status == "Completed": | |
130 print | |
131 print ('Your run was successfully completed. Please check your email for ' | |
132 'results of the run.') | |
133 print | |
134 return 0 | |
135 elif status == "Completed with failures": | |
136 print | |
137 raise CtTrybotException( | |
138 'Your run was completed with failures. Please check your email for ' | |
139 'links to logs of the run.') | |
140 | |
141 print ('The current status of the task %s is "%s". You can view the size ' | |
142 'of the queue here: %s' % (task_id, status, CTFE_QUEUE)) | |
143 print 'Checking again after %s seconds' % POLLING_FREQUENCY_SECS | |
144 print | |
145 time.sleep(POLLING_FREQUENCY_SECS) | |
146 | |
147 | |
148 if '__main__' == __name__: | |
149 option_parser = optparse.OptionParser() | |
150 option_parser.add_option( | |
151 '', '--issue', | |
152 help='The Rietveld CL number to get the patch from.') | |
borenet
2015/09/24 18:42:46
Looks like we're missing the patchset option?
rmistry
2015/09/24 18:51:18
I initially thought it would always use only the l
| |
153 option_parser.add_option( | |
154 '', '--requester', | |
155 help='Email address of the user who requested this run.') | |
156 option_parser.add_option( | |
157 '', '--benchmark', | |
158 help='The CT benchmark to run on the patch.') | |
159 option_parser.add_option( | |
160 '', '--local', default=False, action='store_true', | |
161 help='Uses a dummy metadata salt if this flag is true else it tries to ' | |
162 'get the salt from GCE metadata.') | |
163 options, unused_args = option_parser.parse_args() | |
164 if not options.issue or not options.requester or not options.benchmark: | |
165 option_parser.error('Must specify issue, requester and benchmark') | |
166 | |
167 sys.exit(TriggerAndWait(options)) | |
168 | |
OLD | NEW |