2 # Copyright 2014 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 that
4 # can be found in the LICENSE file.
6 """Automated maintenance tool to run a script on bots.
8 To use this script, write a self-contained python script (use a .par if
9 necessary), specify it on the command line and it will be packaged and triggered
10 on all the swarming bots corresponding to the --dimension filters specified, or
11 all the bots if no filter is specified.
22 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24 # Must be first import.
25 import parallel_execution
27 from third_party import colorama
28 from third_party.depot_tools import fix_encoding
29 from utils import tools
32 def get_bot_list(swarming_server, dimensions, dead_only):
33 """Returns a list of swarming bots."""
35 sys.executable, 'swarming.py', 'query',
36 '--swarming', swarming_server,
39 for k, v in dimensions.iteritems():
40 cmd.extend(('--dimension', k, v))
42 cmd.append('--dead-only')
43 return subprocess.check_output(cmd, cwd=ROOT_DIR).splitlines()
46 def archive(isolate_server, script):
47 """Archives the tool and return the sha-1."""
48 base_script = os.path.basename(script)
51 'command': ['python', base_script],
52 'isolate_dependency_tracked': [base_script],
55 tempdir = tempfile.mkdtemp(prefix='run_on_bots')
57 isolate_file = os.path.join(tempdir, 'tool.isolate')
58 isolated_file = os.path.join(tempdir, 'tool.isolated')
59 with open(isolate_file, 'wb') as f:
61 shutil.copyfile(script, os.path.join(tempdir, base_script))
63 sys.executable, 'isolate.py', 'archive',
64 '--isolate-server', isolate_server,
68 return subprocess.check_output(cmd, cwd=ROOT_DIR).split()[0]
70 shutil.rmtree(tempdir)
74 swarming_server, isolate_server, priority, deadline, repeat, isolated_hash,
76 """Runs the task one at a time.
78 This will be mainly bound by task scheduling latency, especially if the slaves
79 are busy and the priority is low.
82 now = parallel_execution.timestamp()
83 for i in xrange(repeat):
85 # Use an unique task name to ensure the task is executed.
86 suffix = '/%d' % i if repeat > 1 else ''
87 task_name = parallel_execution.unique_task_to_name(
88 name, {'id': bot}, isolated_hash, now) + suffix
90 sys.executable, 'swarming.py', 'run',
91 '--swarming', swarming_server,
92 '--isolate-server', isolate_server,
93 '--priority', priority,
94 '--deadline', deadline,
95 '--dimension', 'id', bot,
96 '--task-name', task_name,
99 r = subprocess.call(cmd, cwd=ROOT_DIR)
100 result = max(r, result)
105 swarming_server, isolate_server, priority, deadline, repeat, isolated_hash,
107 now = parallel_execution.timestamp()
109 for i in xrange(repeat):
110 suffix = '/%d' % i if repeat > 1 else ''
113 parallel_execution.unique_task_to_name(
114 name, {'id': bot}, isolated_hash, now) + suffix,
118 extra_args = ['--priority', priority, '--deadline', deadline]
119 print('Using priority %s' % priority)
120 for failed_task in parallel_execution.run_swarming_tasks_parallel(
121 swarming_server, isolate_server, extra_args, tasks):
122 _name, dimensions, stdout = failed_task
123 print('%sFailure: %s%s\n%s' % (
124 colorama.Fore.RED, dimensions, colorama.Fore.RESET, stdout))
128 parser = parallel_execution.OptionParser(
129 usage='%prog [options] script.py', version=__version__)
131 '--serial', action='store_true',
132 help='Runs the task serially, to be used when debugging problems since '
135 '--repeat', type='int', default=1,
136 help='Runs the task multiple time on each bot, meant to be used as a '
138 options, args = parser.parse_args()
142 'Must pass one python script to run. Use --help for more details')
144 if not options.priority:
146 'Please provide the --priority option. Either use a very low number\n'
147 'so the task completes as fast as possible, or an high number so the\n'
148 'task only runs when the bot is idle.')
150 # 1. Query the bots list.
151 bots = get_bot_list(options.swarming, options.dimensions, False)
152 print('Found %d bots to process' % len(bots))
156 dead_bots = get_bot_list(options.swarming, options.dimensions, True)
158 print('Warning: found %d dead bots' % len(dead_bots))
160 # 2. Archive the script to run.
161 isolated_hash = archive(options.isolate_server, args[0])
162 print('Running %s' % isolated_hash)
164 # 3. Trigger the tasks.
165 name = os.path.basename(args[0])
169 options.isolate_server,
170 str(options.priority),
171 str(options.deadline),
179 options.isolate_server,
180 str(options.priority),
181 str(options.deadline),
188 if __name__ == '__main__':
189 fix_encoding.fix_encoding()
190 tools.disable_buffering()