blob: 85073a913c9931e8880a97fed07f0ab7007f4251 [file] [log] [blame]
#!/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
class ArgumentError(Error):
pass
def GetArgs(cmd_args):
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.', default=os.environ.get('ISOLATE_SERVER', ''))
parser.add_argument('--swarming-server', help='Optional. The swarming server '
'to use.', default=os.environ.get('SWARMING_SERVER', ''))
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(cmd_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):
"""Calls isolate.py archive with the given args."""
cmd = [
sys.executable,
ISOLATE_PY,
'archive',
'--isolated', isolated,
]
cmd.extend(['--isolate-server', isolate_server])
print ' '.join(cmd)
return RunCommand(cmd).split()[0] # The isolated hash
def GetSwarmingCommandLine(args, extra_args):
"""Builds and returns the command line for swarming.py run|trigger."""
cmd = [
sys.executable,
SWARMING_PY,
args.action,
args.controller_isolated,
]
cmd.extend(['--isolate-server', args.isolate_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('--')
cmd.extend(extra_args)
cmd.extend(['--swarming-server', args.swarming_server])
cmd.extend(['--isolate-server', args.isolate_server])
# Specify the output dir
cmd.extend(['--output-dir', '${ISOLATED_OUTDIR}'])
# Task name/hash values
for name, isolated in args.tasks:
if args.format_only:
cmd.extend(['--' + name, isolated + '_test_only'])
else:
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():
if '--' not in sys.argv:
cmd_args = sys.argv[1:]
extra_args = []
else:
index = sys.argv.index('--')
cmd_args = sys.argv[1:index]
extra_args = sys.argv[index+1:]
args = GetArgs(cmd_args)
if not args.swarming_server:
raise ArgumentError('Missing required argument: --swarming-server')
if not args.isolate_server:
raise ArgumentError('Missing required argument: --isolate-server')
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, extra_args)
if not args.format_only:
RunCommand(cmd, True)
return 0
if __name__ == '__main__':
sys.exit(main())