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

Side by Side Diff: swarm_trigger_step.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 | « swarm_trigger_and_get_results.py ('k') | swarming.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 """Triggers a Swarm request based off of a .isolated file.
7
8 This script takes a .isolated file, packages it, and sends a Swarm manifest file
9 to the Swarm server. This is expected to be called as a build step with the cwd
10 as the parent of the src/ directory.
11 """
12
13 import binascii
14 import hashlib
15 import json
16 import optparse
17 import os
18 import sys
19 import time
20 import urllib
21
22 import run_isolated
23
24 from utils import tools
25 from utils import zip_package
26
27
28 ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
29 TOOLS_PATH = os.path.join(ROOT_DIR, 'tools')
30
31
32 PLATFORM_MAPPING = {
33 'cygwin': 'Windows',
34 'darwin': 'Mac',
35 'linux2': 'Linux',
36 'win32': 'Windows',
37 }
38
39
40 class Failure(Exception):
41 pass
42
43
44 class Manifest(object):
45 def __init__(
46 self, manifest_hash, test_name, shards, test_filter, os_image,
47 working_dir, data_server, verbose, profile, priority):
48 """Populates a manifest object.
49 Args:
50 manifest_hash - The manifest's sha-1 that the slave is going to fetch.
51 test_name - The name to give the test request.
52 shards - The number of swarm shards to request.
53 test_filter - The gtest filter to apply when running the test.
54 os_image - OS to run on.
55 working_dir - Relative working directory to start the script.
56 data_server - isolate server url.
57 verbose - if True, have the slave print more details.
58 profile - if True, have the slave print more timing data.
59 priority - int between 0 and 1000, lower the higher priority
60 """
61 self.manifest_hash = manifest_hash
62 self.bundle = zip_package.ZipPackage(ROOT_DIR)
63
64 self._test_name = test_name
65 self._shards = shards
66 self._test_filter = test_filter
67 self._target_platform = PLATFORM_MAPPING[os_image]
68 self._working_dir = working_dir
69
70 base_url = data_server.rstrip('/')
71 self.data_server_retrieval = base_url + '/content/retrieve/default/'
72 self._data_server_storage = base_url + '/content/store/default/'
73 self._data_server_has = base_url + '/content/contains/default'
74 self._data_server_get_token = base_url + '/content/get_token'
75
76 self.verbose = bool(verbose)
77 self.profile = bool(profile)
78 self.priority = priority
79
80 self._zip_file_hash = ''
81 self._tasks = []
82 self._token_cache = None
83
84 def _token(self):
85 if not self._token_cache:
86 result = run_isolated.url_open(self._data_server_get_token)
87 if not result:
88 # TODO(maruel): Implement authentication.
89 raise Failure('Failed to get token, need authentication')
90 # Quote it right away, so creating the urls is simpler.
91 self._token_cache = urllib.quote(result.read())
92 return self._token_cache
93
94 def add_task(self, task_name, actions, time_out=600):
95 """Appends a new task to the swarm manifest file."""
96 # See swarming/src/common/test_request_message.py TestObject constructor for
97 # the valid flags.
98 self._tasks.append(
99 {
100 'action': actions,
101 'decorate_output': self.verbose,
102 'test_name': task_name,
103 'time_out': time_out,
104 })
105
106 def zip_and_upload(self):
107 """Zips up all the files necessary to run a shard and uploads to Swarming
108 master.
109 """
110 assert not self._zip_file_hash
111
112 start_time = time.time()
113 zip_contents = self.bundle.zip_into_buffer()
114 self._zip_file_hash = hashlib.sha1(zip_contents).hexdigest()
115 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time)
116
117 response = run_isolated.url_open(
118 self._data_server_has + '?token=%s' % self._token(),
119 data=binascii.unhexlify(self._zip_file_hash),
120 content_type='application/octet-stream')
121 if response is None:
122 print >> sys.stderr, (
123 'Unable to query server for zip file presence, aborting.')
124 return False
125
126 if response.read(1) == chr(1):
127 print 'Zip file already on server, no need to reupload.'
128 return True
129
130 print 'Zip file not on server, starting uploading.'
131
132 url = '%s%s?priority=0&token=%s' % (
133 self._data_server_storage, self._zip_file_hash, self._token())
134 response = run_isolated.url_open(
135 url, data=zip_contents, content_type='application/octet-stream')
136 if response is None:
137 print >> sys.stderr, 'Failed to upload the zip file: %s' % url
138 return False
139
140 return True
141
142 def to_json(self):
143 """Exports the current configuration into a swarm-readable manifest file.
144
145 This function doesn't mutate the object.
146 """
147 test_case = {
148 'test_case_name': self._test_name,
149 'data': [
150 [self.data_server_retrieval + urllib.quote(self._zip_file_hash),
151 'swarm_data.zip'],
152 ],
153 'tests': self._tasks,
154 'env_vars': {},
155 'configurations': [
156 {
157 'min_instances': self._shards,
158 'config_name': self._target_platform,
159 'dimensions': {
160 'os': self._target_platform,
161 },
162 },
163 ],
164 'working_dir': self._working_dir,
165 'restart_on_failure': True,
166 'cleanup': 'root',
167 'priority': self.priority,
168 }
169
170 # These flags are googletest specific.
171 if self._test_filter and self._test_filter != '*':
172 test_case['env_vars']['GTEST_FILTER'] = self._test_filter
173 if self._shards > 1:
174 test_case['env_vars']['GTEST_SHARD_INDEX'] = '%(instance_index)s'
175 test_case['env_vars']['GTEST_TOTAL_SHARDS'] = '%(num_instances)s'
176
177 return json.dumps(test_case, separators=(',',':'))
178
179
180 def chromium_setup(manifest):
181 """Sets up the commands to run.
182
183 Highly chromium specific.
184 """
185 # Add uncompressed zip here. It'll be compressed as part of the package sent
186 # to Swarming server.
187 run_test_name = 'run_isolated.zip'
188 manifest.bundle.add_buffer(run_test_name,
189 run_isolated.get_as_zip_package().zip_into_buffer(compress=False))
190
191 cleanup_script_name = 'swarm_cleanup.py'
192 manifest.bundle.add_file(os.path.join(TOOLS_PATH, cleanup_script_name),
193 cleanup_script_name)
194
195 run_cmd = [
196 'python', run_test_name,
197 '--hash', manifest.manifest_hash,
198 '--remote', manifest.data_server_retrieval.rstrip('/') + '-gzip/',
199 ]
200 if manifest.verbose or manifest.profile:
201 # Have it print the profiling section.
202 run_cmd.append('--verbose')
203 manifest.add_task('Run Test', run_cmd)
204
205 # Clean up
206 manifest.add_task('Clean Up', ['python', cleanup_script_name])
207
208
209 def process_manifest(
210 file_sha1, test_name, shards, test_filter, os_image, working_dir,
211 data_server, swarm_url, verbose, profile, priority):
212 """Process the manifest file and send off the swarm test request."""
213 try:
214 manifest = Manifest(
215 file_sha1, test_name, shards, test_filter, os_image, working_dir,
216 data_server, verbose, profile, priority)
217 except ValueError as e:
218 print >> sys.stderr, 'Unable to process %s: %s' % (test_name, e)
219 return 1
220
221 chromium_setup(manifest)
222
223 # Zip up relevent files
224 print "Zipping up files..."
225 if not manifest.zip_and_upload():
226 return 1
227
228 # Send test requests off to swarm.
229 print('Sending test requests to swarm.')
230 print('Server: %s' % swarm_url)
231 print('Job name: %s' % test_name)
232 test_url = swarm_url.rstrip('/') + '/test'
233 manifest_text = manifest.to_json()
234 result = run_isolated.url_open(test_url, data={'request': manifest_text})
235 if not result:
236 print >> sys.stderr, 'Failed to send test for %s\n%s' % (
237 test_name, test_url)
238 return 1
239 try:
240 json.load(result)
241 except (ValueError, TypeError) as e:
242 print >> sys.stderr, 'Failed to send test for %s' % test_name
243 print >> sys.stderr, 'Manifest: %s' % manifest_text
244 print >> sys.stderr, str(e)
245 return 1
246 return 0
247
248
249 def main(argv):
250 tools.disable_buffering()
251 parser = optparse.OptionParser(
252 usage='%prog [options]', description=sys.modules[__name__].__doc__)
253 parser.add_option('-w', '--working_dir', default='swarm_tests',
254 help='Desired working direction on the swarm slave side. '
255 'Defaults to %default.')
256 parser.add_option('-o', '--os_image',
257 help='Swarm OS image to request.')
258 parser.add_option('-u', '--swarm-url', default='http://localhost:8080',
259 help='Specify the url of the Swarm server. '
260 'Defaults to %default')
261 parser.add_option('-d', '--data-server',
262 help='The server where all the test data is stored.')
263 parser.add_option('-t', '--test-name-prefix', default='',
264 help='Specify the prefix to give the swarm test request. '
265 'Defaults to %default')
266 parser.add_option('--run_from_hash', nargs=4, action='append', default=[],
267 help='Specify a hash to run on swarm. The format is '
268 '(hash, hash_test_name, shards, test_filter). This may be '
269 'used multiple times to send multiple hashes.')
270 parser.add_option('-v', '--verbose', action='store_true',
271 help='Print verbose logging')
272 parser.add_option('--profile', action='store_true',
273 default=bool(os.environ.get('ISOLATE_DEBUG')),
274 help='Have run_isolated.py print profiling info')
275 parser.add_option('--priority', type='int', default=100,
276 help='The lower value, the more important the task is')
277 (options, args) = parser.parse_args(argv)
278
279 if args:
280 parser.error('Unknown args: %s' % args)
281
282 if not options.os_image or options.os_image == 'None':
283 # This means the Try Server/user wants to use the current OS.
284 options.os_image = sys.platform
285 if not options.data_server:
286 parser.error('Must specify the data directory')
287
288 if not options.run_from_hash:
289 parser.error('At least one --run_from_hash is required.')
290
291 highest_exit_code = 0
292 try:
293 # Send off the hash swarm test requests.
294 for (file_sha1, test_name, shards, testfilter) in options.run_from_hash:
295 exit_code = process_manifest(
296 file_sha1,
297 options.test_name_prefix + test_name,
298 int(shards),
299 testfilter,
300 options.os_image,
301 options.working_dir,
302 options.data_server,
303 options.swarm_url,
304 options.verbose,
305 options.profile,
306 options.priority)
307 highest_exit_code = max(highest_exit_code, exit_code)
308 except Failure as e:
309 print >> sys.stderr, e.args[0]
310 highest_exit_code = max(1, highest_exit_code)
311 return highest_exit_code
312
313
314 if __name__ == '__main__':
315 sys.exit(main(None))
OLDNEW
« no previous file with comments | « swarm_trigger_and_get_results.py ('k') | swarming.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698