Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / testing / legion / tools / legion.py
blob7170f2c6f852bd65bc80765565e2ebff5e85e5ec
1 #!/usr/bin/env python
2 # Copyright 2015 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.
6 """A helper module to run Legion multi-machine tests.
8 Example usage with 1 task machine:
9 $ testing/legion/tools/legion.py run \
10 --controller-isolated out/Release/example_test_controller.isolated \
11 --dimension os Ubuntu-14.04 \
12 --task-name test-task-name \
13 --task task_machine out/Release/example_task_machine.isolated
15 Example usage with 2 task machines with the same isolated file:
16 $ testing/legion/tools/legion.py run \
17 --controller-isolated out/Release/example_test_controller.isolated \
18 --dimension os Ubuntu-14.04 \
19 --task-name test-task-name \
20 --task task_machine_1 out/Release/example_task_machine.isolated \
21 --task task_machine_2 out/Release/example_task_machine.isolated
23 Example usage with 2 task machines with different isolated file:
24 $ testing/legion/tools/legion.py run \
25 --controller-isolated out/Release/example_test_controller.isolated \
26 --dimension os Ubuntu-14.04 \
27 --task-name test-task-name \
28 --task task_machine_1 out/Release/example_task_machine_1.isolated \
29 --task task_machine_2 out/Release/example_task_machine_2.isolated
30 """
32 import argparse
33 import logging
34 import os
35 import subprocess
36 import sys
39 THIS_DIR = os.path.split(__file__)[0]
40 SWARMING_DIR = os.path.join(THIS_DIR, '..', '..', '..', 'tools',
41 'swarming_client')
42 ISOLATE_PY = os.path.join(SWARMING_DIR, 'isolate.py')
43 SWARMING_PY = os.path.join(SWARMING_DIR, 'swarming.py')
44 LOGGING_LEVELS = ['DEBUG', 'INFO', 'WARNING', 'ERROR']
47 class Error(Exception):
48 pass
51 class ArgumentError(Error):
52 pass
55 def GetArgs():
56 parser = argparse.ArgumentParser(description=__doc__)
57 parser.add_argument('action', choices=['run', 'trigger'],
58 help='The swarming action to perform.')
59 parser.add_argument('-f', '--format-only', action='store_true',
60 help='If true the .isolated files are archived but '
61 'swarming is not called, only the command line is built.')
62 parser.add_argument('--controller-isolated', required=True,
63 help='The isolated file for the test controller.')
64 parser.add_argument('--isolate-server', help='Optional. The isolated server '
65 'to use.', default=os.environ.get('ISOLATE_SERVER', ''))
66 parser.add_argument('--swarming-server', help='Optional. The swarming server '
67 'to use.', default=os.environ.get('SWARMING_SERVER', ''))
68 parser.add_argument('--task-name', help='Optional. The swarming task name '
69 'to use.')
70 parser.add_argument('--dimension', action='append', dest='dimensions',
71 nargs=2, default=[], help='Dimensions to pass to '
72 'swarming.py. This is in the form of --dimension key '
73 'value. The minimum required is --dimension os <OS>')
74 parser.add_argument('--task', action='append', dest='tasks',
75 nargs=2, default=[], help='List of task names used in '
76 'the test controller. This is in the form of --task name '
77 '.isolated and is passed to the controller as --name '
78 '<ISOLATED HASH>.')
79 parser.add_argument('--controller-var', action='append',
80 dest='controller_vars', nargs=2, default=[],
81 help='Command line vars to pass to the controller. These '
82 'are in the form of --controller-var name value and are '
83 'passed to the controller as --name value.')
84 parser.add_argument('-v', '--verbosity', default=0, action='count')
85 return parser.parse_args()
88 def RunCommand(cmd, stream_stdout=False):
89 """Runs the command line and streams stdout if requested."""
90 kwargs = {
91 'args': cmd,
92 'stderr': subprocess.PIPE,
94 if not stream_stdout:
95 kwargs['stdout'] = subprocess.PIPE
97 p = subprocess.Popen(**kwargs)
98 stdout, stderr = p.communicate()
99 if p.returncode:
100 raise Error(stderr)
101 if not stream_stdout:
102 logging.debug(stdout)
103 return stdout
106 def Archive(isolated, isolate_server):
107 """Calls isolate.py archive with the given args."""
108 cmd = [
109 sys.executable,
110 ISOLATE_PY,
111 'archive',
112 '--isolated', isolated,
114 cmd.extend(['--isolate-server', isolate_server])
115 print ' '.join(cmd)
116 return RunCommand(cmd).split()[0] # The isolated hash
119 def GetSwarmingCommandLine(args):
120 """Builds and returns the command line for swarming.py run|trigger."""
121 cmd = [
122 sys.executable,
123 SWARMING_PY,
124 args.action,
125 args.controller_isolated,
127 cmd.extend(['--isolate-server', args.isolate_server])
128 cmd.extend(['--swarming', args.swarming_server])
129 if args.task_name:
130 cmd.extend(['--task-name', args.task_name])
131 # swarming.py dimensions
132 for name, value in args.dimensions:
133 cmd.extend(['--dimension', name, value])
135 cmd.append('--')
137 cmd.extend(['--swarming-server', args.swarming_server])
138 cmd.extend(['--isolate-server', args.isolate_server])
139 # Specify the output dir
140 cmd.extend(['--output-dir', '${ISOLATED_OUTDIR}'])
141 # Task name/hash values
142 for name, isolated in args.tasks:
143 cmd.extend(['--' + name, Archive(isolated, args.isolate_server)])
144 # Test controller args
145 for name, value in args.controller_vars:
146 cmd.extend(['--' + name, value])
147 print ' '.join(cmd)
148 return cmd
151 def main():
152 args = GetArgs()
153 if not args.swarming_server:
154 raise ArgumentError('Missing required argument: --swarming-server')
155 if not args.isolate_server:
156 raise ArgumentError('Missing required argument: --isolate-server')
157 logging.basicConfig(
158 format='%(asctime)s %(filename)s:%(lineno)s %(levelname)s] %(message)s',
159 datefmt='%H:%M:%S',
160 level=LOGGING_LEVELS[len(LOGGING_LEVELS)-args.verbosity-1])
161 cmd = GetSwarmingCommandLine(args)
162 if not args.format_only:
163 RunCommand(cmd, True)
164 return 0
167 if __name__ == '__main__':
168 sys.exit(main())