Index: testing/legion/tools/legion.py |
diff --git a/testing/legion/tools/legion.py b/testing/legion/tools/legion.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..7b671335eeb86928b1a244ed740e10bd8a5f88b4 |
--- /dev/null |
+++ b/testing/legion/tools/legion.py |
@@ -0,0 +1,159 @@ |
+#!/usr/bin/env python |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""A helper module to run Legion multi-machine tests. |
+ |
+Example usage with 1 task machine: |
+$ testing/legion/tools/legion.py run \ |
+ --controller-isolated out/Release/example_test_controller.isolated \ |
+ --dimension os Ubuntu-14.04 \ |
+ --task-name test-task-name \ |
+ --task task_machine out/Release/example_task_machine.isolated |
+ |
+Example usage with 2 task machines with the same isolated file: |
+$ testing/legion/tools/legion.py run \ |
+ --controller-isolated out/Release/example_test_controller.isolated \ |
+ --dimension os Ubuntu-14.04 \ |
+ --task-name test-task-name \ |
+ --task task_machine_1 out/Release/example_task_machine.isolated \ |
+ --task task_machine_2 out/Release/example_task_machine.isolated |
+ |
+Example usage with 2 task machines with different isolated file: |
+$ testing/legion/tools/legion.py run \ |
+ --controller-isolated out/Release/example_test_controller.isolated \ |
+ --dimension os Ubuntu-14.04 \ |
+ --task-name test-task-name \ |
+ --task task_machine_1 out/Release/example_task_machine_1.isolated \ |
+ --task task_machine_2 out/Release/example_task_machine_2.isolated |
+""" |
+ |
+import argparse |
+import logging |
+import os |
+import subprocess |
+import sys |
+ |
+ |
+THIS_DIR = os.path.split(__file__)[0] |
+SWARMING_DIR = os.path.join(THIS_DIR, '..', '..', '..', 'tools', |
+ 'swarming_client') |
+ISOLATE_PY = os.path.join(SWARMING_DIR, 'isolate.py') |
+SWARMING_PY = os.path.join(SWARMING_DIR, 'swarming.py') |
+LOGGING_LEVELS = ['DEBUG', 'INFO', 'WARNING', 'ERROR'] |
+ |
+ |
+class Error(Exception): |
+ pass |
+ |
+ |
+def GetArgs(): |
+ parser = argparse.ArgumentParser(description=__doc__) |
+ parser.add_argument('action', choices=['run', 'trigger'], |
+ help='The swarming action to perform.') |
+ parser.add_argument('-f', '--format-only', action='store_true', |
+ help='If true the .isolated files are archived but ' |
+ 'swarming is not called, only the command line is built.') |
+ parser.add_argument('--controller-isolated', required=True, |
+ help='The isolated file for the test controller.') |
+ parser.add_argument('--isolate-server', help='Optional. The isolated server ' |
+ 'to use.') |
+ parser.add_argument('--swarming-server', help='Optional. The swarming server ' |
+ 'to use.') |
+ parser.add_argument('--task-name', help='Optional. The swarming task name ' |
+ 'to use.') |
+ parser.add_argument('--dimension', action='append', dest='dimensions', |
+ nargs=2, default=[], help='Dimensions to pass to ' |
+ 'swarming.py. This is in the form of --dimension key ' |
+ 'value. The minimum required is --dimension os <OS>') |
+ parser.add_argument('--task', action='append', dest='tasks', |
+ nargs=2, default=[], help='List of task names used in ' |
+ 'the test controller. This is in the form of --task name ' |
+ '.isolated and is passed to the controller as --name ' |
+ '<ISOLATED HASH>.') |
+ parser.add_argument('--controller-var', action='append', |
+ dest='controller_vars', nargs=2, default=[], |
+ help='Command line vars to pass to the controller. These ' |
+ 'are in the form of --controller-var name value and are ' |
+ 'passed to the controller as --name value.') |
+ parser.add_argument('-v', '--verbosity', default=0, action='count') |
+ return parser.parse_args() |
+ |
+ |
+def RunCommand(cmd, stream_stdout=False): |
+ """Runs the command line and streams stdout if requested.""" |
+ kwargs = { |
+ 'args': cmd, |
+ 'stderr': subprocess.PIPE, |
+ } |
+ if not stream_stdout: |
+ kwargs['stdout'] = subprocess.PIPE |
+ |
+ p = subprocess.Popen(**kwargs) |
+ stdout, stderr = p.communicate() |
+ if p.returncode: |
+ raise Error(stderr) |
+ if not stream_stdout: |
+ logging.debug(stdout) |
+ return stdout |
+ |
+ |
+def Archive(isolated, isolate_server=None): |
+ """Calls isolate.py archive with the given args.""" |
+ cmd = [ |
+ sys.executable, |
+ ISOLATE_PY, |
+ 'archive', |
+ '--isolated', isolated, |
+ ] |
+ if isolate_server: |
+ cmd.extend(['--isolate-server', isolate_server]) |
+ print ' '.join(cmd) |
+ return RunCommand(cmd).split()[0] # The isolated hash |
+ |
+ |
+def GetSwarmingCommandLine(args): |
+ """Builds and returns the command line for swarming.py run|trigger.""" |
+ cmd = [ |
+ sys.executable, |
+ SWARMING_PY, |
+ args.action, |
+ args.controller_isolated, |
+ ] |
+ if args.isolate_server: |
+ cmd.extend(['--isolate-server', args.isolate_server]) |
+ if args.swarming_server: |
+ cmd.extend(['--swarming', args.swarming_server]) |
+ if args.task_name: |
+ cmd.extend(['--task-name', args.task_name]) |
+ # swarming.py dimensions |
+ for name, value in args.dimensions: |
+ cmd.extend(['--dimension', name, value]) |
+ |
+ cmd.append('--') |
+ |
+ # Task name/hash values |
+ for name, isolated in args.tasks: |
+ cmd.extend(['--' + name, Archive(isolated, args.isolate_server)]) |
+ # Test controller args |
+ for name, value in args.controller_vars: |
+ cmd.extend(['--' + name, value]) |
+ print ' '.join(cmd) |
+ return cmd |
+ |
+ |
+def main(): |
+ args = GetArgs() |
+ logging.basicConfig( |
+ format='%(asctime)s %(filename)s:%(lineno)s %(levelname)s] %(message)s', |
+ datefmt='%H:%M:%S', |
+ level=LOGGING_LEVELS[len(LOGGING_LEVELS)-args.verbosity-1]) |
+ cmd = GetSwarmingCommandLine(args) |
+ if not args.format_only: |
+ RunCommand(cmd, True) |
+ return 0 |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main()) |