blob: 9f31e3516e29f5605f08509cc912de6bff928690 [file] [log] [blame]
maruel@chromium.org0437a732013-08-27 16:05:52 +00001#!/usr/bin/env python
Marc-Antoine Ruel8add1242013-11-05 17:28:27 -05002# Copyright 2013 The Swarming Authors. All rights reserved.
Marc-Antoine Ruele98b1122013-11-05 20:27:57 -05003# Use of this source code is governed under the Apache License, Version 2.0 that
4# can be found in the LICENSE file.
maruel@chromium.org0437a732013-08-27 16:05:52 +00005
6"""Client tool to trigger tasks or retrieve results from a Swarming server."""
7
Vadim Shtayura86a2cef2014-04-18 11:13:39 -07008__version__ = '0.4.5'
maruel@chromium.org0437a732013-08-27 16:05:52 +00009
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -040010import datetime
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -050011import getpass
maruel@chromium.org0437a732013-08-27 16:05:52 +000012import hashlib
13import json
14import logging
15import os
maruel@chromium.org0437a732013-08-27 16:05:52 +000016import shutil
maruel@chromium.org0437a732013-08-27 16:05:52 +000017import subprocess
18import sys
19import time
20import urllib
maruel@chromium.org0437a732013-08-27 16:05:52 +000021
22from third_party import colorama
23from third_party.depot_tools import fix_encoding
24from third_party.depot_tools import subcommand
vadimsh@chromium.org6b706212013-08-28 15:03:46 +000025
Marc-Antoine Ruel8806e622014-02-12 14:15:53 -050026from utils import file_path
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -040027from third_party.chromium import natsort
vadimsh@chromium.org6b706212013-08-28 15:03:46 +000028from utils import net
maruel@chromium.org0437a732013-08-27 16:05:52 +000029from utils import threading_utils
vadimsh@chromium.org6b706212013-08-28 15:03:46 +000030from utils import tools
31from utils import zip_package
maruel@chromium.org0437a732013-08-27 16:05:52 +000032
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080033import auth
maruel@chromium.org7b844a62013-09-17 13:04:59 +000034import isolateserver
maruel@chromium.org0437a732013-08-27 16:05:52 +000035import run_isolated
36
37
38ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
39TOOLS_PATH = os.path.join(ROOT_DIR, 'tools')
40
41
maruel@chromium.org0437a732013-08-27 16:05:52 +000042# The default time to wait for a shard to finish running.
csharp@chromium.org24758492013-08-28 19:10:54 +000043DEFAULT_SHARD_WAIT_TIME = 80 * 60.
maruel@chromium.org0437a732013-08-27 16:05:52 +000044
Vadim Shtayura86a2cef2014-04-18 11:13:39 -070045# How often to print status updates to stdout in 'collect'.
46STATUS_UPDATE_INTERVAL = 15 * 60.
47
maruel@chromium.org0437a732013-08-27 16:05:52 +000048
49NO_OUTPUT_FOUND = (
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -050050 'No output produced by the task, it may have failed to run.\n'
maruel@chromium.org0437a732013-08-27 16:05:52 +000051 '\n')
52
53
maruel@chromium.org0437a732013-08-27 16:05:52 +000054class Failure(Exception):
55 """Generic failure."""
56 pass
57
58
59class Manifest(object):
60 """Represents a Swarming task manifest.
61
62 Also includes code to zip code and upload itself.
63 """
64 def __init__(
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -050065 self, isolate_server, namespace, isolated_hash, task_name, shards, env,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -040066 dimensions, working_dir, deadline, verbose, profile, priority):
maruel@chromium.org0437a732013-08-27 16:05:52 +000067 """Populates a manifest object.
68 Args:
Marc-Antoine Ruela7049872013-11-05 19:28:35 -050069 isolate_server - isolate server url.
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -050070 namespace - isolate server namespace to use.
maruel@chromium.org814d23f2013-10-01 19:08:00 +000071 isolated_hash - The manifest's sha-1 that the slave is going to fetch.
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -050072 task_name - The name to give the task request.
73 shards - The number of swarming shards to request.
Marc-Antoine Ruel05dab5e2013-11-06 15:06:47 -050074 env - environment variables to set.
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -050075 dimensions - dimensions to filter the task on.
maruel@chromium.org0437a732013-08-27 16:05:52 +000076 working_dir - Relative working directory to start the script.
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -040077 deadline - maximum pending time before this task expires.
maruel@chromium.org0437a732013-08-27 16:05:52 +000078 verbose - if True, have the slave print more details.
79 profile - if True, have the slave print more timing data.
maruel@chromium.org7b844a62013-09-17 13:04:59 +000080 priority - int between 0 and 1000, lower the higher priority.
maruel@chromium.org0437a732013-08-27 16:05:52 +000081 """
Marc-Antoine Ruela7049872013-11-05 19:28:35 -050082 self.isolate_server = isolate_server
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -050083 self.namespace = namespace
84 # The reason is that swarm_bot doesn't understand compressed data yet. So
85 # the data to be downloaded by swarm_bot is in 'default', independent of
86 # what run_isolated.py is going to fetch.
Marc-Antoine Ruela7049872013-11-05 19:28:35 -050087 self.storage = isolateserver.get_storage(isolate_server, 'default')
88
maruel@chromium.org814d23f2013-10-01 19:08:00 +000089 self.isolated_hash = isolated_hash
vadimsh@chromium.org6b706212013-08-28 15:03:46 +000090 self.bundle = zip_package.ZipPackage(ROOT_DIR)
91
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -050092 self._task_name = task_name
maruel@chromium.org0437a732013-08-27 16:05:52 +000093 self._shards = shards
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -050094 self._env = env.copy()
95 self._dimensions = dimensions.copy()
maruel@chromium.org0437a732013-08-27 16:05:52 +000096 self._working_dir = working_dir
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -040097 self._deadline = deadline
maruel@chromium.org0437a732013-08-27 16:05:52 +000098
maruel@chromium.org0437a732013-08-27 16:05:52 +000099 self.verbose = bool(verbose)
100 self.profile = bool(profile)
101 self.priority = priority
102
vadimsh@chromium.orgf24e5c32013-10-11 21:16:21 +0000103 self._isolate_item = None
maruel@chromium.org0437a732013-08-27 16:05:52 +0000104 self._tasks = []
maruel@chromium.org0437a732013-08-27 16:05:52 +0000105
Marc-Antoine Ruelaf78a902014-03-20 10:42:49 -0400106 def add_task(self, task_name, actions, time_out=2*60*60):
Marc-Antoine Ruel5c720342014-02-21 14:46:14 -0500107 """Appends a new task as a TestObject to the swarming manifest file.
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500108
109 Tasks cannot be added once the manifest was uploaded.
Marc-Antoine Ruel5c720342014-02-21 14:46:14 -0500110
Marc-Antoine Ruelaf78a902014-03-20 10:42:49 -0400111 By default, command will be killed after 2 hours of execution.
112
Marc-Antoine Ruel5c720342014-02-21 14:46:14 -0500113 See TestObject in services/swarming/src/common/test_request_message.py for
114 the valid format.
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500115 """
116 assert not self._isolate_item
maruel@chromium.org0437a732013-08-27 16:05:52 +0000117 self._tasks.append(
118 {
119 'action': actions,
120 'decorate_output': self.verbose,
121 'test_name': task_name,
Marc-Antoine Ruelaf78a902014-03-20 10:42:49 -0400122 'hard_time_out': time_out,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000123 })
124
maruel@chromium.org0437a732013-08-27 16:05:52 +0000125 def to_json(self):
126 """Exports the current configuration into a swarm-readable manifest file.
127
Marc-Antoine Ruel5c720342014-02-21 14:46:14 -0500128 The actual serialization format is defined as a TestCase object as described
129 in services/swarming/src/common/test_request_message.py
130
maruel@chromium.org0437a732013-08-27 16:05:52 +0000131 This function doesn't mutate the object.
132 """
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500133 request = {
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500134 'cleanup': 'root',
maruel@chromium.org0437a732013-08-27 16:05:52 +0000135 'configurations': [
Marc-Antoine Ruel5c720342014-02-21 14:46:14 -0500136 # Is a TestConfiguration.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000137 {
Marc-Antoine Ruel5d799192013-11-06 15:20:39 -0500138 'config_name': 'isolated',
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400139 'deadline_to_run': self._deadline,
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -0500140 'dimensions': self._dimensions,
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500141 'min_instances': self._shards,
142 'priority': self.priority,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000143 },
144 ],
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500145 'data': [],
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500146 'encoding': 'UTF-8',
Marc-Antoine Ruel05dab5e2013-11-06 15:06:47 -0500147 'env_vars': self._env,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000148 'restart_on_failure': True,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500149 'test_case_name': self._task_name,
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500150 'tests': self._tasks,
151 'working_dir': self._working_dir,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000152 }
vadimsh@chromium.orgf24e5c32013-10-11 21:16:21 +0000153 if self._isolate_item:
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500154 request['data'].append(
maruel@chromium.org814d23f2013-10-01 19:08:00 +0000155 [
Vadim Shtayurabcff74f2014-02-27 16:19:34 -0800156 self.storage.get_fetch_url(self._isolate_item),
maruel@chromium.org814d23f2013-10-01 19:08:00 +0000157 'swarm_data.zip',
158 ])
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500159 return json.dumps(request, sort_keys=True, separators=(',',':'))
maruel@chromium.org0437a732013-08-27 16:05:52 +0000160
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500161 @property
162 def isolate_item(self):
163 """Calling this property 'closes' the manifest and it can't be modified
164 afterward.
165 """
166 if self._isolate_item is None:
167 self._isolate_item = isolateserver.BufferItem(
Vadim Shtayurabcff74f2014-02-27 16:19:34 -0800168 self.bundle.zip_into_buffer(), high_priority=True)
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500169 return self._isolate_item
170
171
172def zip_and_upload(manifest):
173 """Zips up all the files necessary to run a manifest and uploads to Swarming
174 master.
175 """
176 try:
177 start_time = time.time()
178 with manifest.storage:
179 uploaded = manifest.storage.upload_items([manifest.isolate_item])
180 elapsed = time.time() - start_time
181 except (IOError, OSError) as exc:
182 tools.report_error('Failed to upload the zip file: %s' % exc)
183 return False
184
185 if manifest.isolate_item in uploaded:
186 logging.info('Upload complete, time elapsed: %f', elapsed)
187 else:
188 logging.info('Zip file already on server, time elapsed: %f', elapsed)
189 return True
190
maruel@chromium.org0437a732013-08-27 16:05:52 +0000191
192def now():
193 """Exists so it can be mocked easily."""
194 return time.time()
195
196
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500197def get_task_keys(swarm_base_url, task_name):
198 """Returns the Swarming task key for each shards of task_name."""
199 key_data = urllib.urlencode([('name', task_name)])
maruel@chromium.org0437a732013-08-27 16:05:52 +0000200 url = '%s/get_matching_test_cases?%s' % (swarm_base_url, key_data)
201
vadimsh@chromium.org043b76d2013-09-12 16:15:13 +0000202 for _ in net.retry_loop(max_attempts=net.URL_OPEN_MAX_ATTEMPTS):
203 result = net.url_read(url, retry_404=True)
204 if result is None:
maruel@chromium.org0437a732013-08-27 16:05:52 +0000205 raise Failure(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500206 'Error: Unable to find any task with the name, %s, on swarming server'
207 % task_name)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000208
maruel@chromium.org0437a732013-08-27 16:05:52 +0000209 # TODO(maruel): Compare exact string.
210 if 'No matching' in result:
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500211 logging.warning('Unable to find any task with the name, %s, on swarming '
212 'server' % task_name)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000213 continue
214 return json.loads(result)
215
216 raise Failure(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500217 'Error: Unable to find any task with the name, %s, on swarming server'
218 % task_name)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000219
220
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500221def retrieve_results(base_url, task_key, timeout, should_stop):
222 """Retrieves results for a single task_key."""
maruel@chromium.org814d23f2013-10-01 19:08:00 +0000223 assert isinstance(timeout, float), timeout
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500224 params = [('r', task_key)]
maruel@chromium.org0437a732013-08-27 16:05:52 +0000225 result_url = '%s/get_result?%s' % (base_url, urllib.urlencode(params))
226 start = now()
227 while True:
228 if timeout and (now() - start) >= timeout:
229 logging.error('retrieve_results(%s) timed out', base_url)
230 return {}
231 # Do retries ourselves.
vadimsh@chromium.org043b76d2013-09-12 16:15:13 +0000232 response = net.url_read(result_url, retry_404=False, retry_50x=False)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000233 if response is None:
234 # Aggressively poll for results. Do not use retry_404 so
235 # should_stop is polled more often.
236 remaining = min(5, timeout - (now() - start)) if timeout else 5
237 if remaining > 0:
maruel@chromium.org814d23f2013-10-01 19:08:00 +0000238 if should_stop.get():
239 return {}
vadimsh@chromium.org043b76d2013-09-12 16:15:13 +0000240 net.sleep_before_retry(1, remaining)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000241 else:
242 try:
vadimsh@chromium.org043b76d2013-09-12 16:15:13 +0000243 data = json.loads(response) or {}
maruel@chromium.org0437a732013-08-27 16:05:52 +0000244 except (ValueError, TypeError):
245 logging.warning(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500246 'Received corrupted data for task_key %s. Retrying.', task_key)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000247 else:
248 if data['output']:
249 return data
250 if should_stop.get():
251 return {}
252
253
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700254def yield_results(
255 swarm_base_url, task_keys, timeout, max_threads, print_status_updates):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500256 """Yields swarming task results from the swarming server as (index, result).
maruel@chromium.org0437a732013-08-27 16:05:52 +0000257
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700258 Duplicate shards are ignored. Shards are yielded in order of completion.
259 Timed out shards are NOT yielded at all. Caller can compare number of yielded
260 shards with len(task_keys) to verify all shards completed.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000261
262 max_threads is optional and is used to limit the number of parallel fetches
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500263 done. Since in general the number of task_keys is in the range <=10, it's not
maruel@chromium.org0437a732013-08-27 16:05:52 +0000264 worth normally to limit the number threads. Mostly used for testing purposes.
Marc-Antoine Ruel5c720342014-02-21 14:46:14 -0500265
266 Yields:
267 (index, result). In particular, 'result' is defined as the
268 GetRunnerResults() function in services/swarming/server/test_runner.py.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000269 """
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500270 shards_remaining = range(len(task_keys))
maruel@chromium.org0437a732013-08-27 16:05:52 +0000271 number_threads = (
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500272 min(max_threads, len(task_keys)) if max_threads else len(task_keys))
maruel@chromium.org0437a732013-08-27 16:05:52 +0000273 should_stop = threading_utils.Bit()
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700274 results_channel = threading_utils.TaskChannel()
275 active_task_count = len(task_keys)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000276 with threading_utils.ThreadPool(number_threads, number_threads, 0) as pool:
277 try:
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500278 for task_key in task_keys:
maruel@chromium.org0437a732013-08-27 16:05:52 +0000279 pool.add_task(
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700280 0, results_channel.wrap_task(retrieve_results),
281 swarm_base_url, task_key, timeout, should_stop)
282 while active_task_count:
283 try:
284 result = results_channel.pull(timeout=STATUS_UPDATE_INTERVAL)
285 except threading_utils.TaskChannel.Timeout:
286 if print_status_updates:
287 print(
288 'Waiting for results from the following shards: %s' %
289 ', '.join(map(str, shards_remaining)))
290 sys.stdout.flush()
291 continue
292 except Exception:
293 logging.exception('Unexpected exception in retrieve_results')
294 result = None
295 active_task_count -= 1
maruel@chromium.org0437a732013-08-27 16:05:52 +0000296 if not result:
297 # Failed to retrieve one key.
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500298 logging.error('Failed to retrieve the results for a swarming key')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000299 continue
300 shard_index = result['config_instance_index']
301 if shard_index in shards_remaining:
302 shards_remaining.remove(shard_index)
303 yield shard_index, result
304 else:
305 logging.warning('Ignoring duplicate shard index %d', shard_index)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000306 finally:
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700307 # Done or aborted with Ctrl+C, kill the remaining threads.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000308 should_stop.set()
309
310
311def chromium_setup(manifest):
312 """Sets up the commands to run.
313
314 Highly chromium specific.
315 """
vadimsh@chromium.org6b706212013-08-28 15:03:46 +0000316 # Add uncompressed zip here. It'll be compressed as part of the package sent
317 # to Swarming server.
318 run_test_name = 'run_isolated.zip'
319 manifest.bundle.add_buffer(run_test_name,
320 run_isolated.get_as_zip_package().zip_into_buffer(compress=False))
maruel@chromium.org0437a732013-08-27 16:05:52 +0000321
vadimsh@chromium.org6b706212013-08-28 15:03:46 +0000322 cleanup_script_name = 'swarm_cleanup.py'
323 manifest.bundle.add_file(os.path.join(TOOLS_PATH, cleanup_script_name),
324 cleanup_script_name)
325
maruel@chromium.org0437a732013-08-27 16:05:52 +0000326 run_cmd = [
327 'python', run_test_name,
maruel@chromium.org814d23f2013-10-01 19:08:00 +0000328 '--hash', manifest.isolated_hash,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500329 '--namespace', manifest.namespace,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000330 ]
Marc-Antoine Ruel8806e622014-02-12 14:15:53 -0500331 if file_path.is_url(manifest.isolate_server):
332 run_cmd.extend(('--isolate-server', manifest.isolate_server))
333 else:
334 run_cmd.extend(('--indir', manifest.isolate_server))
335
maruel@chromium.org0437a732013-08-27 16:05:52 +0000336 if manifest.verbose or manifest.profile:
337 # Have it print the profiling section.
338 run_cmd.append('--verbose')
339 manifest.add_task('Run Test', run_cmd)
340
341 # Clean up
342 manifest.add_task('Clean Up', ['python', cleanup_script_name])
343
344
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500345def googletest_setup(env, shards):
346 """Sets googletest specific environment variables."""
347 if shards > 1:
348 env = env.copy()
349 env['GTEST_SHARD_INDEX'] = '%(instance_index)s'
350 env['GTEST_TOTAL_SHARDS'] = '%(num_instances)s'
351 return env
352
353
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500354def archive(isolate_server, namespace, isolated, algo, verbose):
maruel@chromium.org0437a732013-08-27 16:05:52 +0000355 """Archives a .isolated and all the dependencies on the CAC."""
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500356 logging.info('archive(%s, %s, %s)', isolate_server, namespace, isolated)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000357 tempdir = None
Marc-Antoine Ruel8806e622014-02-12 14:15:53 -0500358 if file_path.is_url(isolate_server):
359 command = 'archive'
360 flag = '--isolate-server'
361 else:
362 command = 'hashtable'
363 flag = '--outdir'
364
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500365 print('Archiving: %s' % isolated)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000366 try:
maruel@chromium.org0437a732013-08-27 16:05:52 +0000367 cmd = [
368 sys.executable,
369 os.path.join(ROOT_DIR, 'isolate.py'),
Marc-Antoine Ruel8806e622014-02-12 14:15:53 -0500370 command,
371 flag, isolate_server,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500372 '--namespace', namespace,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000373 '--isolated', isolated,
374 ]
maruel@chromium.orge9403ab2013-09-20 18:03:49 +0000375 cmd.extend(['--verbose'] * verbose)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000376 logging.info(' '.join(cmd))
377 if subprocess.call(cmd, verbose):
378 return
maruel@chromium.org7b844a62013-09-17 13:04:59 +0000379 return isolateserver.hash_file(isolated, algo)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000380 finally:
381 if tempdir:
382 shutil.rmtree(tempdir)
383
384
385def process_manifest(
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500386 swarming, isolate_server, namespace, isolated_hash, task_name, shards,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400387 dimensions, env, working_dir, deadline, verbose, profile, priority):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500388 """Processes the manifest file and send off the swarming task request."""
maruel@chromium.org0437a732013-08-27 16:05:52 +0000389 try:
390 manifest = Manifest(
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500391 isolate_server=isolate_server,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500392 namespace=namespace,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500393 isolated_hash=isolated_hash,
394 task_name=task_name,
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500395 shards=shards,
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -0500396 dimensions=dimensions,
Marc-Antoine Ruel05dab5e2013-11-06 15:06:47 -0500397 env=env,
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500398 working_dir=working_dir,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400399 deadline=deadline,
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500400 verbose=verbose,
401 profile=profile,
Vadim Shtayurabcff74f2014-02-27 16:19:34 -0800402 priority=priority)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000403 except ValueError as e:
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500404 tools.report_error('Unable to process %s: %s' % (task_name, e))
maruel@chromium.org0437a732013-08-27 16:05:52 +0000405 return 1
406
407 chromium_setup(manifest)
408
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500409 logging.info('Zipping up files...')
410 if not zip_and_upload(manifest):
maruel@chromium.org0437a732013-08-27 16:05:52 +0000411 return 1
412
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500413 logging.info('Server: %s', swarming)
414 logging.info('Task name: %s', task_name)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500415 trigger_url = swarming + '/test'
maruel@chromium.org0437a732013-08-27 16:05:52 +0000416 manifest_text = manifest.to_json()
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500417 result = net.url_read(trigger_url, data={'request': manifest_text})
maruel@chromium.org0437a732013-08-27 16:05:52 +0000418 if not result:
vadimsh@chromium.orgd908a542013-10-30 01:36:17 +0000419 tools.report_error(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500420 'Failed to trigger task %s\n%s' % (task_name, trigger_url))
maruel@chromium.org0437a732013-08-27 16:05:52 +0000421 return 1
422 try:
vadimsh@chromium.orgf24e5c32013-10-11 21:16:21 +0000423 json.loads(result)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000424 except (ValueError, TypeError) as e:
vadimsh@chromium.orgd908a542013-10-30 01:36:17 +0000425 msg = '\n'.join((
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500426 'Failed to trigger task %s' % task_name,
vadimsh@chromium.orgd908a542013-10-30 01:36:17 +0000427 'Manifest: %s' % manifest_text,
428 'Bad response: %s' % result,
429 str(e)))
430 tools.report_error(msg)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000431 return 1
432 return 0
433
434
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500435def isolated_to_hash(isolate_server, namespace, arg, algo, verbose):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500436 """Archives a .isolated file if needed.
437
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500438 Returns the file hash to trigger and a bool specifying if it was a file (True)
439 or a hash (False).
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500440 """
441 if arg.endswith('.isolated'):
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500442 file_hash = archive(isolate_server, namespace, arg, algo, verbose)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500443 if not file_hash:
444 tools.report_error('Archival failure %s' % arg)
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500445 return None, True
446 return file_hash, True
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500447 elif isolateserver.is_valid_hash(arg, algo):
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500448 return arg, False
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500449 else:
450 tools.report_error('Invalid hash %s' % arg)
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500451 return None, False
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500452
453
maruel@chromium.org0437a732013-08-27 16:05:52 +0000454def trigger(
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500455 swarming,
456 isolate_server,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500457 namespace,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500458 file_hash_or_isolated,
459 task_name,
460 shards,
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -0500461 dimensions,
462 env,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500463 working_dir,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400464 deadline,
maruel@chromium.org0437a732013-08-27 16:05:52 +0000465 verbose,
466 profile,
467 priority):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500468 """Sends off the hash swarming task requests."""
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500469 file_hash, is_file = isolated_to_hash(
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500470 isolate_server, namespace, file_hash_or_isolated, hashlib.sha1, verbose)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500471 if not file_hash:
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500472 return 1, ''
473 if not task_name:
474 # If a file name was passed, use its base name of the isolated hash.
475 # Otherwise, use user name as an approximation of a task name.
476 if is_file:
477 key = os.path.splitext(os.path.basename(file_hash_or_isolated))[0]
478 else:
479 key = getpass.getuser()
480 task_name = '%s/%s/%s' % (
481 key,
482 '_'.join('%s=%s' % (k, v) for k, v in sorted(dimensions.iteritems())),
483 file_hash)
484
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500485 env = googletest_setup(env, shards)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500486 # TODO(maruel): It should first create a request manifest object, then pass
487 # it to a function to zip, archive and trigger.
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500488 result = process_manifest(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500489 swarming=swarming,
490 isolate_server=isolate_server,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500491 namespace=namespace,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500492 isolated_hash=file_hash,
493 task_name=task_name,
494 shards=shards,
495 dimensions=dimensions,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400496 deadline=deadline,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500497 env=env,
498 working_dir=working_dir,
499 verbose=verbose,
500 profile=profile,
Vadim Shtayurabcff74f2014-02-27 16:19:34 -0800501 priority=priority)
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500502 return result, task_name
maruel@chromium.org0437a732013-08-27 16:05:52 +0000503
504
505def decorate_shard_output(result, shard_exit_code):
506 """Returns wrapped output for swarming task shard."""
507 tag = 'index %s (machine tag: %s, id: %s)' % (
508 result['config_instance_index'],
509 result['machine_id'],
510 result.get('machine_tag', 'unknown'))
511 return (
512 '\n'
513 '================================================================\n'
514 'Begin output from shard %s\n'
515 '================================================================\n'
516 '\n'
517 '%s'
518 '================================================================\n'
519 'End output from shard %s. Return %d\n'
520 '================================================================\n'
521 ) % (tag, result['output'] or NO_OUTPUT_FOUND, tag, shard_exit_code)
522
523
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700524def collect(url, task_name, timeout, decorate, print_status_updates):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500525 """Retrieves results of a Swarming task."""
526 logging.info('Collecting %s', task_name)
527 task_keys = get_task_keys(url, task_name)
528 if not task_keys:
529 raise Failure('No task keys to get results with.')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000530
maruel@chromium.org9c1c7b52013-08-28 19:04:36 +0000531 exit_code = None
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700532 seen_shards = set()
533 for index, output in yield_results(
534 url, task_keys, timeout, None, print_status_updates):
535 seen_shards.add(index)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000536 shard_exit_codes = (output['exit_codes'] or '1').split(',')
537 shard_exit_code = max(int(i) for i in shard_exit_codes)
538 if decorate:
539 print decorate_shard_output(output, shard_exit_code)
540 else:
541 print(
542 '%s/%s: %s' % (
543 output['machine_id'],
544 output['machine_tag'],
545 output['exit_codes']))
546 print(''.join(' %s\n' % l for l in output['output'].splitlines()))
maruel@chromium.org9c1c7b52013-08-28 19:04:36 +0000547 exit_code = exit_code or shard_exit_code
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700548 if len(seen_shards) != len(task_keys):
549 missing_shards = [x for x in range(len(task_keys)) if x not in seen_shards]
550 print >> sys.stderr, ('Results from some shards are missing: %s' %
551 ', '.join(map(str, missing_shards)))
552 exit_code = exit_code or 1
maruel@chromium.org9c1c7b52013-08-28 19:04:36 +0000553 return exit_code if exit_code is not None else 1
maruel@chromium.org0437a732013-08-27 16:05:52 +0000554
555
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400556def add_filter_options(parser):
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500557 parser.filter_group = tools.optparse.OptionGroup(parser, 'Filtering slaves')
558 parser.filter_group.add_option(
Marc-Antoine Ruelb39e8cf2014-01-20 10:39:31 -0500559 '-d', '--dimension', default=[], action='append', nargs=2,
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -0500560 dest='dimensions', metavar='FOO bar',
561 help='dimension to filter on')
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500562 parser.add_option_group(parser.filter_group)
563
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400564
565def add_trigger_options(parser):
566 """Adds all options to trigger a task on Swarming."""
567 isolateserver.add_isolate_server_options(parser, True)
568 add_filter_options(parser)
569
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500570 parser.task_group = tools.optparse.OptionGroup(parser, 'Task properties')
571 parser.task_group.add_option(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500572 '-w', '--working-dir', default='swarm_tests',
573 help='Working directory on the swarming slave side. default: %default.')
574 parser.task_group.add_option(
575 '--working_dir', help=tools.optparse.SUPPRESS_HELP)
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500576 parser.task_group.add_option(
577 '-e', '--env', default=[], action='append', nargs=2, metavar='FOO bar',
578 help='environment variables to set')
579 parser.task_group.add_option(
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500580 '--priority', type='int', default=100,
581 help='The lower value, the more important the task is')
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500582 parser.task_group.add_option(
583 '--shards', type='int', default=1, help='number of shards to use')
584 parser.task_group.add_option(
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500585 '-T', '--task-name',
586 help='Display name of the task. It uniquely identifies the task. '
587 'Defaults to <base_name>/<dimensions>/<isolated hash> if an '
588 'isolated file is provided, if a hash is provided, it defaults to '
589 '<user>/<dimensions>/<isolated hash>')
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400590 parser.task_group.add_option(
591 '--deadline', type='int', default=6*60*60,
592 help='Seconds to allow the task to be pending for a bot to run before '
593 'this task request expires.')
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500594 parser.add_option_group(parser.task_group)
Marc-Antoine Ruelcd629732013-12-20 15:00:42 -0500595 # TODO(maruel): This is currently written in a chromium-specific way.
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500596 parser.group_logging.add_option(
maruel@chromium.org0437a732013-08-27 16:05:52 +0000597 '--profile', action='store_true',
598 default=bool(os.environ.get('ISOLATE_DEBUG')),
599 help='Have run_isolated.py print profiling info')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000600
601
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500602def process_trigger_options(parser, options, args):
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500603 isolateserver.process_isolate_server_options(parser, options)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500604 if len(args) != 1:
605 parser.error('Must pass one .isolated file or its hash (sha1).')
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500606 options.dimensions = dict(options.dimensions)
Marc-Antoine Ruel2d1bee82014-03-12 14:10:25 -0400607 if not options.dimensions:
608 parser.error('Please at least specify one --dimension')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000609
610
611def add_collect_options(parser):
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500612 parser.server_group.add_option(
maruel@chromium.org0437a732013-08-27 16:05:52 +0000613 '-t', '--timeout',
614 type='float',
615 default=DEFAULT_SHARD_WAIT_TIME,
616 help='Timeout to wait for result, set to 0 for no timeout; default: '
617 '%default s')
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500618 parser.group_logging.add_option(
619 '--decorate', action='store_true', help='Decorate output')
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700620 parser.group_logging.add_option(
621 '--print-status-updates', action='store_true',
622 help='Print periodic status updates')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000623
624
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500625@subcommand.usage('task_name')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000626def CMDcollect(parser, args):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500627 """Retrieves results of a Swarming task.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000628
629 The result can be in multiple part if the execution was sharded. It can
630 potentially have retries.
631 """
632 add_collect_options(parser)
633 (options, args) = parser.parse_args(args)
634 if not args:
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500635 parser.error('Must specify one task name.')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000636 elif len(args) > 1:
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500637 parser.error('Must specify only one task name.')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000638
639 try:
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700640 return collect(
641 options.swarming,
642 args[0],
643 options.timeout,
644 options.decorate,
645 options.print_status_updates)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000646 except Failure as e:
vadimsh@chromium.orgd908a542013-10-30 01:36:17 +0000647 tools.report_error(e)
648 return 1
maruel@chromium.org0437a732013-08-27 16:05:52 +0000649
650
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400651def CMDquery(parser, args):
652 """Returns information about the bots connected to the Swarming server."""
653 add_filter_options(parser)
654 parser.filter_group.add_option(
Marc-Antoine Ruel28083112014-03-13 16:34:04 -0400655 '--dead-only', action='store_true',
656 help='Only print dead bots, useful to reap them and reimage broken bots')
657 parser.filter_group.add_option(
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400658 '-k', '--keep-dead', action='store_true',
659 help='Do not filter out dead bots')
660 parser.filter_group.add_option(
661 '-b', '--bare', action='store_true',
Marc-Antoine Ruele7b00162014-03-12 16:59:01 -0400662 help='Do not print out dimensions')
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400663 options, args = parser.parse_args(args)
Marc-Antoine Ruel28083112014-03-13 16:34:04 -0400664
665 if options.keep_dead and options.dead_only:
666 parser.error('Use only one of --keep-dead and --dead-only')
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400667 service = net.get_http_service(options.swarming)
668 data = service.json_request('GET', '/swarming/api/v1/bots')
669 if data is None:
670 print >> sys.stderr, 'Failed to access %s' % options.swarming
671 return 1
672 timeout = datetime.timedelta(seconds=data['machine_death_timeout'])
673 utcnow = datetime.datetime.utcnow()
674 for machine in natsort.natsorted(data['machines'], key=lambda x: x['tag']):
675 last_seen = datetime.datetime.strptime(
676 machine['last_seen'], '%Y-%m-%d %H:%M:%S')
Marc-Antoine Ruel28083112014-03-13 16:34:04 -0400677 is_dead = utcnow - last_seen > timeout
678 if options.dead_only:
679 if not is_dead:
680 continue
681 elif not options.keep_dead and is_dead:
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400682 continue
683
Marc-Antoine Ruele7b00162014-03-12 16:59:01 -0400684 # If the user requested to filter on dimensions, ensure the bot has all the
685 # dimensions requested.
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400686 dimensions = machine['dimensions']
687 for key, value in options.dimensions:
688 if key not in dimensions:
689 break
Marc-Antoine Ruele7b00162014-03-12 16:59:01 -0400690 # A bot can have multiple value for a key, for example,
691 # {'os': ['Windows', 'Windows-6.1']}, so that --dimension os=Windows will
692 # be accepted.
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400693 if isinstance(dimensions[key], list):
694 if value not in dimensions[key]:
695 break
696 else:
697 if value != dimensions[key]:
698 break
699 else:
700 print machine['tag']
Marc-Antoine Ruele7b00162014-03-12 16:59:01 -0400701 if not options.bare:
Marc-Antoine Ruel819fb162014-03-12 16:38:26 -0400702 print ' %s' % dimensions
703 return 0
704
705
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500706@subcommand.usage('[hash|isolated]')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000707def CMDrun(parser, args):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500708 """Triggers a task and wait for the results.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000709
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500710 Basically, does everything to run a command remotely.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000711 """
712 add_trigger_options(parser)
713 add_collect_options(parser)
714 options, args = parser.parse_args(args)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500715 process_trigger_options(parser, options, args)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000716
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500717 try:
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500718 result, task_name = trigger(
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500719 swarming=options.swarming,
Marc-Antoine Ruel8806e622014-02-12 14:15:53 -0500720 isolate_server=options.isolate_server or options.indir,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500721 namespace=options.namespace,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500722 file_hash_or_isolated=args[0],
723 task_name=options.task_name,
724 shards=options.shards,
725 dimensions=options.dimensions,
726 env=dict(options.env),
727 working_dir=options.working_dir,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400728 deadline=options.deadline,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500729 verbose=options.verbose,
730 profile=options.profile,
731 priority=options.priority)
732 except Failure as e:
733 tools.report_error(
734 'Failed to trigger %s(%s): %s' %
735 (options.task_name, args[0], e.args[0]))
736 return 1
737 if result:
738 tools.report_error('Failed to trigger the task.')
maruel@chromium.org0437a732013-08-27 16:05:52 +0000739 return result
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500740 if task_name != options.task_name:
741 print('Triggered task: %s' % task_name)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500742 try:
743 return collect(
744 options.swarming,
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500745 task_name,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500746 options.timeout,
Vadim Shtayura86a2cef2014-04-18 11:13:39 -0700747 options.decorate,
748 options.print_status_updates)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500749 except Failure as e:
750 tools.report_error(e)
751 return 1
maruel@chromium.org0437a732013-08-27 16:05:52 +0000752
753
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500754@subcommand.usage("(hash|isolated)")
maruel@chromium.org0437a732013-08-27 16:05:52 +0000755def CMDtrigger(parser, args):
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500756 """Triggers a Swarming task.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000757
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500758 Accepts either the hash (sha1) of a .isolated file already uploaded or the
759 path to an .isolated file to archive, packages it if needed and sends a
760 Swarming manifest file to the Swarming server.
761
762 If an .isolated file is specified instead of an hash, it is first archived.
maruel@chromium.org0437a732013-08-27 16:05:52 +0000763 """
764 add_trigger_options(parser)
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500765 options, args = parser.parse_args(args)
766 process_trigger_options(parser, options, args)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000767
768 try:
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500769 result, task_name = trigger(
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500770 swarming=options.swarming,
Marc-Antoine Ruel8806e622014-02-12 14:15:53 -0500771 isolate_server=options.isolate_server or options.indir,
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500772 namespace=options.namespace,
Marc-Antoine Ruel7c543272013-11-26 13:26:15 -0500773 file_hash_or_isolated=args[0],
774 task_name=options.task_name,
775 dimensions=options.dimensions,
776 shards=options.shards,
Marc-Antoine Ruel92f32422013-11-06 18:12:13 -0500777 env=dict(options.env),
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500778 working_dir=options.working_dir,
Marc-Antoine Ruel13b7b782014-03-14 11:14:57 -0400779 deadline=options.deadline,
Marc-Antoine Ruela7049872013-11-05 19:28:35 -0500780 verbose=options.verbose,
781 profile=options.profile,
782 priority=options.priority)
Marc-Antoine Ruel5b475782014-02-14 20:57:59 -0500783 if task_name != options.task_name and not result:
784 print('Triggered task: %s' % task_name)
785 return result
maruel@chromium.org0437a732013-08-27 16:05:52 +0000786 except Failure as e:
vadimsh@chromium.orgd908a542013-10-30 01:36:17 +0000787 tools.report_error(e)
788 return 1
maruel@chromium.org0437a732013-08-27 16:05:52 +0000789
790
791class OptionParserSwarming(tools.OptionParserWithLogging):
792 def __init__(self, **kwargs):
793 tools.OptionParserWithLogging.__init__(
794 self, prog='swarming.py', **kwargs)
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500795 self.server_group = tools.optparse.OptionGroup(self, 'Server')
796 self.server_group.add_option(
maruel@chromium.orge9403ab2013-09-20 18:03:49 +0000797 '-S', '--swarming',
Kevin Graney5346c162014-01-24 12:20:01 -0500798 metavar='URL', default=os.environ.get('SWARMING_SERVER', ''),
maruel@chromium.orge9403ab2013-09-20 18:03:49 +0000799 help='Swarming server to use')
Marc-Antoine Ruel5471e3d2013-11-11 19:10:32 -0500800 self.add_option_group(self.server_group)
Vadim Shtayurae34e13a2014-02-02 11:23:26 -0800801 auth.add_auth_options(self)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000802
803 def parse_args(self, *args, **kwargs):
804 options, args = tools.OptionParserWithLogging.parse_args(
805 self, *args, **kwargs)
806 options.swarming = options.swarming.rstrip('/')
807 if not options.swarming:
808 self.error('--swarming is required.')
Vadim Shtayura5d1efce2014-02-04 10:55:43 -0800809 auth.process_auth_options(self, options)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000810 return options, args
811
812
813def main(args):
814 dispatcher = subcommand.CommandDispatcher(__name__)
815 try:
816 return dispatcher.execute(OptionParserSwarming(version=__version__), args)
vadimsh@chromium.orgd908a542013-10-30 01:36:17 +0000817 except Exception as e:
818 tools.report_error(e)
maruel@chromium.org0437a732013-08-27 16:05:52 +0000819 return 1
820
821
822if __name__ == '__main__':
823 fix_encoding.fix_encoding()
824 tools.disable_buffering()
825 colorama.init()
826 sys.exit(main(sys.argv[1:]))