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

Side by Side Diff: scripts/slave/run_slavelastic.py

Issue 10035003: Split up Each Swarm Test into Two Steps (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 8 years, 8 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
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 # run_slavelastic.py: Runs a test based off of a slavelastic manifest file. 5 # run_slavelastic.py: Runs a test based off of a slavelastic manifest file.
6 6
7 from __future__ import with_statement 7 from __future__ import with_statement
8 import json # pylint: disable=F0401 8 import json
9 import optparse 9 import optparse
10 import os 10 import os
11 import platform 11 import platform
12 import random 12 import random
13 import socket 13 import socket
14 import sys 14 import sys
15 import time 15 import time
16 import urllib2 16 import urllib2
17 import zipfile 17 import zipfile
18 18
(...skipping 21 matching lines...) Expand all
40 } 40 }
41 41
42 # This can cause problems when 42 # This can cause problems when
43 # |current_platform| != |switches_dict['os_image']| 43 # |current_platform| != |switches_dict['os_image']|
44 # crbug.com/117442 44 # crbug.com/117442
45 current_platform = platform_mapping[sys.platform] 45 current_platform = platform_mapping[sys.platform]
46 switches_dict = { 46 switches_dict = {
47 'num_shards': switches.num_shards, 47 'num_shards': switches.num_shards,
48 'os_image': current_platform, 48 'os_image': current_platform,
49 } 49 }
50 self.name = filename 50 self.manifest_name = filename
51 51
52 self.g_shards = switches.num_shards 52 self.g_shards = switches.num_shards
53 # Random name for the output zip file 53 # Random name for the output zip file
54 self.zipfile_name = 'swarm_tempfile_%s.zip' % ''.join(random.choice( 54 self.zipfile_name = 'swarm_tempfile_%s.zip' % ''.join(random.choice(
55 'abcdefghijklmnopqrstuvwxyz0123456789') for x in range(10)) 55 'abcdefghijklmnopqrstuvwxyz0123456789') for x in range(10))
56 self.tasks = [] 56 self.tasks = []
57 self.current_platform = current_platform 57 self.current_platform = current_platform
58 self.target_platform = switches_dict['os_image'] 58 self.target_platform = switches_dict['os_image']
59 self.working_dir = switches.working_dir 59 self.working_dir = switches.working_dir
60 self.test_name = switches.test_name
60 61
61 def add_task(self, task_name, actions): 62 def add_task(self, task_name, actions):
62 """Appends a new task to the swarm manifest file.""" 63 """Appends a new task to the swarm manifest file."""
63 self.tasks.append({ 64 self.tasks.append({
64 'test_name': task_name, 65 'test_name': task_name,
65 'action': actions, 66 'action': actions,
66 }) 67 })
67 68
68 def zip(self): 69 def zip(self):
69 """Zip up all the files in self.files""" 70 """Zip up all the files in self.files"""
70 start_time = time.time() 71 start_time = time.time()
71 72
72 zip_file = zipfile.ZipFile(self.zipfile_name, 'w') 73 zip_file = zipfile.ZipFile(self.zipfile_name, 'w')
73 zip_file.write(self.name) 74 zip_file.write(self.manifest_name)
74 zip_file.write(self.run_test_path) 75 zip_file.write(self.run_test_path)
75 zip_file.close() 76 zip_file.close()
76 77
77 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time) 78 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time)
78 79
79 def cleanup(self): 80 def cleanup(self):
80 os.remove(self.zipfile_name) 81 os.remove(self.zipfile_name)
81 82
82 def to_json(self): 83 def to_json(self):
83 """Export the current configuration into a swarm-readable manifest file""" 84 """Export the current configuration into a swarm-readable manifest file"""
84 hostname = socket.gethostbyname(socket.gethostname()) 85 hostname = socket.gethostbyname(socket.gethostname())
85 # pylint: disable=E1103 86 # pylint: disable=E1103
86 filepath = os.path.relpath(self.zipfile_name, '../..').replace('\\', '/') 87 filepath = os.path.relpath(self.zipfile_name, '../..').replace('\\', '/')
87 88
88 url = 'http://%s/hashtable/' % hostname 89 url = 'http://%s/hashtable/' % hostname
89 self.add_task( 90 self.add_task(
90 'Run Test', 91 'Run Test',
91 ['python', self.run_test_path, '-m', self.name, '-r', url]) 92 ['python', self.run_test_path, '-m', self.manifest_name, '-r', url])
92 93
93 # Clean up 94 # Clean up
94 if self.current_platform == 'Linux' or self.current_platform == 'Mac': 95 if self.current_platform == 'Linux' or self.current_platform == 'Mac':
95 cleanup_commands = ['rm', '-rf'] 96 cleanup_commands = ['rm', '-rf']
96 elif self.current_platform == 'Windows': 97 elif self.current_platform == 'Windows':
97 cleanup_commands = ['del'] 98 cleanup_commands = ['del']
98 self.add_task('Clean Up', cleanup_commands + [self.zipfile_name]) 99 self.add_task('Clean Up', cleanup_commands + [self.zipfile_name])
99 100
100 # Call kill_processes.py if on windows 101 # Call kill_processes.py if on windows
101 if self.target_platform == 'Windows': 102 if self.target_platform == 'Windows':
102 self.add_task('Kill Processes', 103 self.add_task('Kill Processes',
103 [sys.executable, '..\\b\\build\\scripts\\slave\\kill_processes.py']) 104 [sys.executable, '..\\b\\build\\scripts\\slave\\kill_processes.py'])
104 105
105 # Construct test case 106 # Construct test case
106 test_case = { 107 test_case = {
107 'test_case_name': self.name, 108 'test_case_name': self.test_name,
108 'data': [ 109 'data': [
109 'http://%s/%s' % (hostname, filepath), 110 'http://%s/%s' % (hostname, filepath),
110 ], 111 ],
111 'tests': self.tasks, 112 'tests': self.tasks,
112 'env_vars': { 113 'env_vars': {
113 'GTEST_TOTAL_SHARDS': '%(num_instances)s', 114 'GTEST_TOTAL_SHARDS': '%(num_instances)s',
114 'GTEST_SHARD_INDEX': '%(instance_index)s', 115 'GTEST_SHARD_INDEX': '%(instance_index)s',
115 }, 116 },
116 'configurations': [ 117 'configurations': [
117 { 118 {
118 'min_instances': self.g_shards, 119 'min_instances': self.g_shards,
119 'max_instances': self.g_shards, 120 'max_instances': self.g_shards,
120 'config_name': self.target_platform, 121 'config_name': self.target_platform,
121 'dimensions': { 122 'dimensions': {
122 'os': self.target_platform, 123 'os': self.target_platform,
123 }, 124 },
124 }, 125 },
125 ], 126 ],
126 'working_dir': self.working_dir, 127 'working_dir': self.working_dir,
127 'cleanup': 'data', 128 'cleanup': 'data',
128 } 129 }
129 130
130 return json.dumps(test_case) 131 return json.dumps(test_case)
131 132
132 133
133 def _get_first_number(line):
134 for part in line.split():
135 if part.isdigit():
136 return int(part)
137
138 print 'No number in :'
139 print line
140 return 0
141
142
143 class TestSummary(object):
144 def __init__(self):
145 self.test_passed_count = 0
146 self.failed_tests = []
147 self.disabled_test_count = 0
148 self.ignored_test_count = 0
149
150 def AddSummaryData(self, buf):
151 lines = buf.splitlines()
152
153 for line in lines:
154 if '[ PASSED ]' in line:
155 self.test_passed_count += _get_first_number(line)
156 elif '[ FAILED ]' in line:
157 if ', listed below' not in line:
158 self.failed_tests.append(line)
159 elif 'DISABLED' in line:
160 self.disabled_test_count += _get_first_number(line)
161 elif 'failures' in line:
162 self.ignored_test_count += _get_first_number(line)
163
164 def Output(self):
165 output = []
166
167 output.append('[ PASSED ] %i tests.' % self.test_passed_count)
168 if self.failed_tests:
169 output.append('[ FAILED ] failed tests listed below:')
170 output.extend(self.failed_tests)
171 output.append('%i FAILED TESTS' % len(self.failed_tests))
172
173 if self.disabled_test_count:
174 output.append('%i DISABLED TESTS' % self.disabled_test_count)
175
176 if self.ignored_test_count:
177 output.append('%i tests with ignored failures (FAILS prefix)' %
178 self.ignored_test_count)
179
180 return output
181
182 def exit_code(self):
183 return int(bool(self.failed_tests))
184
185
186 # TODO(csharp) The sharing_supervisor.py also has test parsing code, they should
187 # be shared.
188 def TestRunOutput(output):
189 """Go through the given output and only return the output from the Test Run
190 Step.
191 """
192 test_run_output = []
193
194 in_step = False
195 step_name = ''
196 for line in output.splitlines():
197 if in_step:
198 if '[ OK ] ' + step_name in line:
199 break
200 else:
201 test_run_output.append(line)
202 elif '[ RUN ] ' in line and 'Run Test' in line:
203 in_step = True
204 i = len('[ RUN ] ')
205 step_name = line[i:]
206
207 return '\n'.join(test_run_output)
208
209
210 def main(): 134 def main():
211 """Packages up a Slavelastic test and send it to swarm. Receive output from 135 """Packages up a Slavelastic test and send it to swarm. Receive output from
212 all shards and print it to stdout. 136 all shards and print it to stdout.
213 137
214 Args 138 Args
215 slavelastic manifest file 139 slavelastic manifest file
216 number of shards 140 number of shards
217 ... 141 ...
218 """ 142 """
219 # Parses arguments 143 # Parses arguments
(...skipping 10 matching lines...) Expand all
230 'greater than or equal to min_shards.') 154 'greater than or equal to min_shards.')
231 parser.add_option('-o', '--os_image', 155 parser.add_option('-o', '--os_image',
232 help='Swarm OS image to request. Defaults to the ' 156 help='Swarm OS image to request. Defaults to the '
233 'current platform.') 157 'current platform.')
234 parser.add_option('-n', '--hostname', default='localhost', 158 parser.add_option('-n', '--hostname', default='localhost',
235 help='Specify the hostname of the Swarm server. ' 159 help='Specify the hostname of the Swarm server. '
236 'Defaults to %default') 160 'Defaults to %default')
237 parser.add_option('-p', '--port', type='int', default=8080, 161 parser.add_option('-p', '--port', type='int', default=8080,
238 help='Specify the port of the Swarm server. ' 162 help='Specify the port of the Swarm server. '
239 'Defaults to %default') 163 'Defaults to %default')
164 parser.add_option('-t', '--test_name',
165 help='Specify the name to give the swarm test request. '
166 'Defaults to the given filename')
240 parser.add_option('-v', '--verbose', action='store_true', 167 parser.add_option('-v', '--verbose', action='store_true',
241 help='Print verbose logging') 168 help='Print verbose logging')
242 (options, args) = parser.parse_args() 169 (options, args) = parser.parse_args()
243 if not args: 170 if not args:
244 parser.error('Must specify one filename.') 171 parser.error('Must specify one filename.')
245 elif len(args) > 1: 172 elif len(args) > 1:
246 parser.error('Must specify only one filename.') 173 parser.error('Must specify only one filename.')
247 filename = args[0] 174 filename = args[0]
248 if not options.os_image: 175 if not options.os_image:
249 options.os_image = '%s %d' % (platform.uname()[0], 32) 176 options.os_image = '%s %d' % (platform.uname()[0], 32)
177 if not options.test_name:
178 options.test_name = filename
250 179
251 # Parses manifest file 180 # Parses manifest file
252 print "Parsing file %s..." % filename 181 print "Parsing file %s..." % filename
253 manifest = Manifest(filename, options) 182 manifest = Manifest(filename, options)
254 183
255 # Zip up relevent files 184 # Zip up relevent files
256 print "Zipping up files..." 185 print "Zipping up files..."
257 manifest.zip() 186 manifest.zip()
258 187
259 # Send test requests off to swarm. 188 # Send test requests off to swarm.
260 print 'Sending test requests to swarm' 189 print 'Sending test requests to swarm'
261 base_url = 'http://%s:%d' % (options.hostname, options.port) 190 base_url = 'http://%s:%d' % (options.hostname, options.port)
262 test_url = base_url + '/test' 191 test_url = base_url + '/test'
263 manifest_text = manifest.to_json() 192 manifest_text = manifest.to_json()
264 result = urllib2.urlopen(test_url, manifest_text).read() 193 result = urllib2.urlopen(test_url, manifest_text).read()
265 194
266 # Check that we can read the output as a JSON string 195 # Check that we can read the output as a JSON string
267 try: 196 try:
268 test_keys = json.loads(result) 197 json.loads(result)
269 except (ValueError, TypeError), e: 198 except (ValueError, TypeError), e:
270 print 'Request failed:' 199 print 'Request failed:'
271 print result 200 print result
201 print e
272 return 1 202 return 1
273 203
274 running_test_keys = test_keys['test_keys'] 204 return 0
275
276 # TODO(csharp) Get hostnames from key through swarm
277 hostnames = ['localhost' for i in range(options.num_shards)]
278
279 # TODO(csharp) Get exit codes from key through swarm
280 exit_codes = [0 for i in range(options.num_shards)]
281
282 # Listen to output_destination
283 summary_total = TestSummary()
284 for index in range(options.num_shards):
285 print
286 print '===================================================================='
287 print 'Begin output from shard index %d (%s)' % (index, hostnames[i])
288 print '===================================================================='
289 print
290 while True:
291 try:
292 key_url = '%s/get_result?r=%s' % (base_url,
293 running_test_keys[index]['test_key'])
294 output = urllib2.urlopen(key_url).read()
295
296 if output:
297 cleaned_output = TestRunOutput(output)
298 summary_index = cleaned_output.rfind('[ PASSED ]')
299 summary_total.AddSummaryData(cleaned_output[summary_index:])
300 sys.stdout.write(cleaned_output[:summary_index - 1])
301 break
302 else:
303 # Test is not yet done, wait a bit before checking again.
304 time.sleep(0.5)
305 except urllib2.HTTPError, e:
306 print 'Calling %s threw %s' % (key_url, e)
307 print
308 print '===================================================================='
309 print 'End output from shard index %d (%s). Return %d' % (index,
310 hostnames[i], exit_codes[i])
311 print '===================================================================='
312 print
313 manifest.cleanup() # Delete temp zip file
314
315 print '\n'.join(summary_total.Output())
316 print
317
318 if options.verbose:
319 print 'All tests completed:'
320 for i in range(options.num_shards):
321 print 'Shard index %d (%s): Exit code: %d' % (i,
322 hostnames[i], exit_codes[i])
323
324 # TODO(csharp) replace with max exit code once exit_codes gets real values
325 return summary_total.exit_code()
326 205
327 if __name__ == '__main__': 206 if __name__ == '__main__':
328 sys.exit(main()) 207 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698