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

Side by Side Diff: swarm_get_results.py

Issue 22980008: Merge all swarm_*.py scripts into swarming.py. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: Rebase against r219402 Created 7 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « example/run_example_swarm.py ('k') | swarm_trigger_and_get_results.py » ('j') | 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) 2012 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 """Retrieves all the output that the Swarm server has produced for requests with
7 that name.
8 """
9
10 import json
11 import logging
12 import optparse
13 import sys
14 import time
15 import urllib
16
17 from third_party.depot_tools import fix_encoding
18
19 import run_isolated
20
21 from utils import threading_utils
22 from utils import tools
23
24
25 # The default time to wait for a shard to finish running.
26 DEFAULT_SHARD_WAIT_TIME = 40 * 60.
27
28
29 class Failure(Exception):
30 """Generic failure."""
31 pass
32
33
34 def get_test_keys(swarm_base_url, test_name, _=None):
35 """Returns the Swarm test key for each shards of test_name."""
36 # TODO(maruel): Remove the parameter '_' once the
37 # build/scripts/slave/get_swarm_results.py stops passing it.
38 key_data = urllib.urlencode([('name', test_name)])
39 url = '%s/get_matching_test_cases?%s' % (swarm_base_url, key_data)
40
41 for i in range(run_isolated.URL_OPEN_MAX_ATTEMPTS):
42 response = run_isolated.url_open(url, retry_404=True)
43 if response is None:
44 raise Failure(
45 'Error: Unable to find any tests with the name, %s, on swarm server'
46 % test_name)
47
48 result = response.read()
49 # TODO(maruel): Compare exact string.
50 if 'No matching' in result:
51 logging.warning('Unable to find any tests with the name, %s, on swarm '
52 'server' % test_name)
53 if i != run_isolated.URL_OPEN_MAX_ATTEMPTS:
54 run_isolated.HttpService.sleep_before_retry(i, None)
55 continue
56 return json.loads(result)
57
58 raise Failure(
59 'Error: Unable to find any tests with the name, %s, on swarm server'
60 % test_name)
61
62
63 def now():
64 """Exists so it can be mocked easily."""
65 return time.time()
66
67
68 def retrieve_results(base_url, test_key, timeout, should_stop):
69 """Retrieves results for a single test_key."""
70 assert isinstance(timeout, float)
71 params = [('r', test_key)]
72 result_url = '%s/get_result?%s' % (base_url, urllib.urlencode(params))
73 start = now()
74 while True:
75 if timeout and (now() - start) >= timeout:
76 logging.error('retrieve_results(%s) timed out', base_url)
77 return {}
78 # Do retries ourselves.
79 response = run_isolated.url_open(
80 result_url, retry_404=False, retry_50x=False)
81 if response is None:
82 # Aggressively poll for results. Do not use retry_404 so
83 # should_stop is polled more often.
84 remaining = min(5, timeout - (now() - start)) if timeout else 5
85 if remaining > 0:
86 run_isolated.HttpService.sleep_before_retry(1, remaining)
87 else:
88 try:
89 data = json.load(response) or {}
90 except (ValueError, TypeError):
91 logging.warning(
92 'Received corrupted data for test_key %s. Retrying.', test_key)
93 else:
94 if data['output']:
95 return data
96 if should_stop.get():
97 return {}
98
99
100 def yield_results(swarm_base_url, test_keys, timeout, max_threads):
101 """Yields swarm test results from the swarm server as (index, result).
102
103 Duplicate shards are ignored, the first one to complete is returned.
104
105 max_threads is optional and is used to limit the number of parallel fetches
106 done. Since in general the number of test_keys is in the range <=10, it's not
107 worth normally to limit the number threads. Mostly used for testing purposes.
108 """
109 shards_remaining = range(len(test_keys))
110 number_threads = (
111 min(max_threads, len(test_keys)) if max_threads else len(test_keys))
112 should_stop = threading_utils.Bit()
113 results_remaining = len(test_keys)
114 with threading_utils.ThreadPool(number_threads, number_threads, 0) as pool:
115 try:
116 for test_key in test_keys:
117 pool.add_task(
118 0, retrieve_results, swarm_base_url, test_key, timeout, should_stop)
119 while shards_remaining and results_remaining:
120 result = pool.get_one_result()
121 results_remaining -= 1
122 if not result:
123 # Failed to retrieve one key.
124 logging.error('Failed to retrieve the results for a swarm key')
125 continue
126 shard_index = result['config_instance_index']
127 if shard_index in shards_remaining:
128 shards_remaining.remove(shard_index)
129 yield shard_index, result
130 else:
131 logging.warning('Ignoring duplicate shard index %d', shard_index)
132 # Pop the last entry, there's no such shard.
133 shards_remaining.pop()
134 finally:
135 # Done, kill the remaining threads.
136 should_stop.set()
137
138
139 def parse_args():
140 tools.disable_buffering()
141 parser = optparse.OptionParser(
142 usage='%prog [options] test_name',
143 description=sys.modules[__name__].__doc__)
144 parser.add_option(
145 '-u', '--url', default='http://localhost:8080',
146 help='Specify the url of the Swarm server, defaults: %default')
147 parser.add_option(
148 '-v', '--verbose', action='store_true',
149 help='Print verbose logging')
150 parser.add_option(
151 '-t', '--timeout',
152 type='float',
153 default=DEFAULT_SHARD_WAIT_TIME,
154 help='Timeout to wait for result, set to 0 for no timeout; default: '
155 '%default s')
156 # TODO(maruel): Remove once the masters have been updated.
157 parser.add_option(
158 '-s', '--shards',
159 help=optparse.SUPPRESS_HELP)
160
161 (options, args) = parser.parse_args()
162 if not args:
163 parser.error('Must specify one test name.')
164 elif len(args) > 1:
165 parser.error('Must specify only one test name.')
166 options.url = options.url.rstrip('/')
167 logging.basicConfig(level=logging.DEBUG if options.verbose else logging.ERROR)
168 return parser, options, args[0]
169
170
171 def main():
172 parser, options, test_name = parse_args()
173 try:
174 test_keys = get_test_keys(options.url, test_name)
175 except Failure as e:
176 parser.error(e.args[0])
177 if not test_keys:
178 parser.error('No test keys to get results with.')
179
180 options.shards = len(test_keys) if options.shards == -1 else options.shards
181 exit_code = None
182 for _index, output in yield_results(
183 options.url, test_keys, options.timeout, None):
184 print(
185 '%s/%s: %s' % (
186 output['machine_id'], output['machine_tag'], output['exit_codes']))
187 print(''.join(' %s\n' % l for l in output['output'].splitlines()))
188 exit_code = max(exit_code, max(map(int, output['exit_codes'].split(','))))
189
190 return exit_code
191
192
193 if __name__ == '__main__':
194 fix_encoding.fix_encoding()
195 sys.exit(main())
OLDNEW
« no previous file with comments | « example/run_example_swarm.py ('k') | swarm_trigger_and_get_results.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698