Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # Copyright (c) 2013 The Chromium OS 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. |
| 5 | |
| 6 | import argparse |
Aviv Keshet | 15782de | 2013-07-31 11:40:41 -0700 | [diff] [blame] | 7 | import errno |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 8 | import os |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 9 | import re |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 10 | import signal |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 11 | import subprocess |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 12 | import sys |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 13 | import tempfile |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 14 | import threading |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 15 | |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 16 | import logging |
| 17 | # Turn the logging level to INFO before importing other autotest |
| 18 | # code, to avoid having failed import logging messages confuse the |
| 19 | # test_that user. |
| 20 | logging.basicConfig(level=logging.INFO) |
| 21 | |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 22 | import common |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 23 | from autotest_lib.client.common_lib.cros import dev_server, retry |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 24 | from autotest_lib.client.common_lib import logging_manager |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 25 | from autotest_lib.server.cros.dynamic_suite import suite |
| 26 | from autotest_lib.server.cros.dynamic_suite import constants |
| 27 | from autotest_lib.server import autoserv_utils |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 28 | from autotest_lib.server import server_logging_config |
| 29 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 30 | |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 31 | try: |
| 32 | from chromite.lib import cros_build_lib |
| 33 | except ImportError: |
| 34 | print 'Unable to import chromite.' |
| 35 | print 'This script must be either:' |
| 36 | print ' - Be run in the chroot.' |
| 37 | print ' - (not yet supported) be run after running ' |
| 38 | print ' ../utils/build_externals.py' |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 39 | |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 40 | _autoserv_proc = None |
| 41 | _sigint_handler_lock = threading.Lock() |
| 42 | |
| 43 | _AUTOSERV_SIGINT_TIMEOUT_SECONDS = 5 |
Alex Miller | a091307 | 2013-06-12 10:01:51 -0700 | [diff] [blame] | 44 | _NO_BOARD = 'ad_hoc_board' |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 45 | _NO_BUILD = 'ad_hoc_build' |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 46 | |
Aviv Keshet | 2dc9893 | 2013-06-17 15:22:10 -0700 | [diff] [blame] | 47 | _QUICKMERGE_SCRIPTNAME = '/mnt/host/source/chromite/bin/autotest_quickmerge' |
| 48 | |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 49 | _TEST_REPORT_SCRIPTNAME = '/usr/bin/generate_test_report' |
| 50 | |
Aviv Keshet | c9e7462 | 2013-07-18 10:11:11 -0700 | [diff] [blame] | 51 | _LATEST_RESULTS_DIRECTORY = '/tmp/test_that_latest' |
| 52 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 53 | |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 54 | def schedule_local_suite(autotest_path, suite_name, afe, build=_NO_BUILD, |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 55 | board=_NO_BOARD, results_directory=None, |
| 56 | no_experimental=False): |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 57 | """ |
| 58 | Schedule a suite against a mock afe object, for a local suite run. |
| 59 | @param autotest_path: Absolute path to autotest (in sysroot). |
| 60 | @param suite_name: Name of suite to schedule. |
| 61 | @param afe: afe object to schedule against (typically a directAFE) |
| 62 | @param build: Build to schedule suite for. |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 63 | @param board: Board to schedule suite for. |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 64 | @param results_directory: Absolute path of directory to store results in. |
| 65 | (results will be stored in subdirectory of this). |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 66 | @param no_experimental: Skip experimental tests when scheduling a suite. |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 67 | @returns: The number of tests scheduled. |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 68 | """ |
| 69 | fs_getter = suite.Suite.create_fs_getter(autotest_path) |
| 70 | devserver = dev_server.ImageServer('') |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 71 | my_suite = suite.Suite.create_from_name(suite_name, build, board, |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 72 | devserver, fs_getter, afe=afe, ignore_deps=True, |
| 73 | results_dir=results_directory) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 74 | if len(my_suite.tests) == 0: |
| 75 | raise ValueError('Suite named %s does not exist, or contains no ' |
| 76 | 'tests.' % suite_name) |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 77 | # Schedule tests, discard record calls. |
| 78 | return my_suite.schedule(lambda x: None, |
| 79 | add_experimental=not no_experimental) |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 80 | |
| 81 | |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 82 | def schedule_local_test(autotest_path, test_name, afe, build=_NO_BUILD, |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 83 | board=_NO_BOARD, results_directory=None): |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 84 | #temporarily disabling pylint |
| 85 | #pylint: disable-msg=C0111 |
| 86 | """ |
| 87 | Schedule an individual test against a mock afe object, for a local run. |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 88 | @param autotest_path: Absolute path to autotest (in sysroot). |
| 89 | @param test_name: Name of test to schedule. |
| 90 | @param afe: afe object to schedule against (typically a directAFE) |
| 91 | @param build: Build to schedule suite for. |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 92 | @param board: Board to schedule suite for. |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 93 | @param results_directory: Absolute path of directory to store results in. |
| 94 | (results will be stored in subdirectory of this). |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 95 | @returns: The number of tests scheduled (may be >1 if there are |
| 96 | multiple tests with the same name). |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 97 | """ |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 98 | fs_getter = suite.Suite.create_fs_getter(autotest_path) |
| 99 | devserver = dev_server.ImageServer('') |
| 100 | predicates = [suite.Suite.test_name_equals_predicate(test_name)] |
| 101 | suite_name = 'suite_' + test_name |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 102 | my_suite = suite.Suite.create_from_predicates(predicates, build, board, |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 103 | devserver, fs_getter, afe=afe, name=suite_name, ignore_deps=True, |
| 104 | results_dir=results_directory) |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 105 | if len(my_suite.tests) == 0: |
| 106 | raise ValueError('No tests named %s.' % test_name) |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 107 | # Schedule tests, discard record calls. |
| 108 | return my_suite.schedule(lambda x: None) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 109 | |
| 110 | |
Aviv Keshet | 5e33c17 | 2013-07-16 05:00:49 -0700 | [diff] [blame] | 111 | def run_job(job, host, sysroot_autotest_path, results_directory, fast_mode, |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 112 | id_digits=1, ssh_verbosity=0, args=None, pretend=False): |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 113 | """ |
| 114 | Shell out to autoserv to run an individual test job. |
| 115 | |
| 116 | @param job: A Job object containing the control file contents and other |
| 117 | relevent metadata for this test. |
| 118 | @param host: Hostname of DUT to run test against. |
| 119 | @param sysroot_autotest_path: Absolute path of autotest directory. |
Aviv Keshet | c882440 | 2013-06-29 20:37:30 -0700 | [diff] [blame] | 120 | @param results_directory: Absolute path of directory to store results in. |
| 121 | (results will be stored in subdirectory of this). |
Christopher Wiley | f6b5aae | 2013-07-09 10:14:02 -0700 | [diff] [blame] | 122 | @param fast_mode: bool to use fast mode (disables slow autotest features). |
Aviv Keshet | 5e33c17 | 2013-07-16 05:00:49 -0700 | [diff] [blame] | 123 | @param id_digits: The minimum number of digits that job ids should be |
| 124 | 0-padded to when formatting as a string for results |
| 125 | directory. |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 126 | @param ssh_verbosity: SSH verbosity level, passed along to autoserv_utils |
Aviv Keshet | 30322f9 | 2013-07-18 13:21:52 -0700 | [diff] [blame] | 127 | @param args: String that should be passed as args parameter to autoserv, |
| 128 | and then ultimitely to test itself. |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 129 | @param pretend: If True, will print out autoserv commands rather than |
| 130 | running them. |
Aviv Keshet | c882440 | 2013-06-29 20:37:30 -0700 | [diff] [blame] | 131 | @returns: Absolute path of directory where results were stored. |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 132 | """ |
| 133 | with tempfile.NamedTemporaryFile() as temp_file: |
| 134 | temp_file.write(job.control_file) |
| 135 | temp_file.flush() |
Aviv Keshet | c882440 | 2013-06-29 20:37:30 -0700 | [diff] [blame] | 136 | results_directory = os.path.join(results_directory, |
Aviv Keshet | 5e33c17 | 2013-07-16 05:00:49 -0700 | [diff] [blame] | 137 | 'results-%0*d' % (id_digits, job.id)) |
Aviv Keshet | 30322f9 | 2013-07-18 13:21:52 -0700 | [diff] [blame] | 138 | extra_args = [temp_file.name] |
| 139 | if args: |
| 140 | extra_args.extend(['--args', args]) |
Aviv Keshet | c882440 | 2013-06-29 20:37:30 -0700 | [diff] [blame] | 141 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 142 | command = autoserv_utils.autoserv_run_job_command( |
| 143 | os.path.join(sysroot_autotest_path, 'server'), |
| 144 | machines=host, job=job, verbose=False, |
Aviv Keshet | c882440 | 2013-06-29 20:37:30 -0700 | [diff] [blame] | 145 | results_directory=results_directory, |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 146 | fast_mode=fast_mode, ssh_verbosity=ssh_verbosity, |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 147 | extra_args=extra_args, |
| 148 | no_console_prefix=True) |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 149 | |
| 150 | if not pretend: |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 151 | logging.debug('Running autoserv command: %s', ' '.join(command)) |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 152 | global _autoserv_proc |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 153 | _autoserv_proc = subprocess.Popen(command, |
| 154 | stdout=subprocess.PIPE, |
| 155 | stderr=subprocess.STDOUT) |
| 156 | # This incantation forces unbuffered reading from stdout, |
| 157 | # so that autoserv output can be displayed to the user |
| 158 | # immediately. |
| 159 | for message in iter(_autoserv_proc.stdout.readline, b''): |
| 160 | logging.info('autoserv| %s', message.strip()) |
| 161 | |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 162 | _autoserv_proc.wait() |
| 163 | _autoserv_proc = None |
| 164 | return results_directory |
| 165 | else: |
| 166 | logging.info('Pretend mode. Would run autoserv command: %s', |
| 167 | ' '.join(command)) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 168 | |
| 169 | |
| 170 | def setup_local_afe(): |
| 171 | """ |
| 172 | Setup a local afe database and return a direct_afe object to access it. |
| 173 | |
| 174 | @returns: A autotest_lib.frontend.afe.direct_afe instance. |
| 175 | """ |
| 176 | # This import statement is delayed until now rather than running at |
| 177 | # module load time, because it kicks off a local sqlite :memory: backed |
| 178 | # database, and we don't need that unless we are doing a local run. |
| 179 | from autotest_lib.frontend import setup_django_lite_environment |
| 180 | from autotest_lib.frontend.afe import direct_afe |
| 181 | return direct_afe.directAFE() |
| 182 | |
| 183 | |
Christopher Wiley | f6b5aae | 2013-07-09 10:14:02 -0700 | [diff] [blame] | 184 | def perform_local_run(afe, autotest_path, tests, remote, fast_mode, |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 185 | build=_NO_BUILD, board=_NO_BOARD, args=None, |
Aviv Keshet | 15782de | 2013-07-31 11:40:41 -0700 | [diff] [blame] | 186 | pretend=False, no_experimental=False, |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 187 | results_directory=None, ssh_verbosity=0): |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 188 | """ |
| 189 | @param afe: A direct_afe object used to interact with local afe database. |
| 190 | @param autotest_path: Absolute path of sysroot installed autotest. |
| 191 | @param tests: List of strings naming tests and suites to run. Suite strings |
| 192 | should be formed like "suite:smoke". |
| 193 | @param remote: Remote hostname. |
Christopher Wiley | f6b5aae | 2013-07-09 10:14:02 -0700 | [diff] [blame] | 194 | @param fast_mode: bool to use fast mode (disables slow autotest features). |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 195 | @param build: String specifying build for local run. |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 196 | @param board: String specifyinb board for local run. |
Aviv Keshet | 30322f9 | 2013-07-18 13:21:52 -0700 | [diff] [blame] | 197 | @param args: String that should be passed as args parameter to autoserv, |
| 198 | and then ultimitely to test itself. |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 199 | @param pretend: If True, will print out autoserv commands rather than |
| 200 | running them. |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 201 | @param no_experimental: Skip experimental tests when scheduling a suite. |
Aviv Keshet | 15782de | 2013-07-31 11:40:41 -0700 | [diff] [blame] | 202 | @param results_directory: Directory to store results in. Defaults to None, |
| 203 | in which case results will be stored in a new |
| 204 | subdirectory of /tmp |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 205 | @param ssh_verbosity: SSH verbosity level, passed through to |
| 206 | autoserv_utils. |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 207 | """ |
| 208 | afe.create_label(constants.VERSION_PREFIX + build) |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 209 | afe.create_label(board) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 210 | afe.create_host(remote) |
| 211 | |
| 212 | # Schedule tests / suites in local afe |
| 213 | for test in tests: |
| 214 | suitematch = re.match(r'suite:(.*)', test) |
| 215 | if suitematch: |
| 216 | suitename = suitematch.group(1) |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 217 | logging.info('Scheduling suite %s...', suitename) |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 218 | ntests = schedule_local_suite(autotest_path, suitename, afe, |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 219 | build=build, board=board, |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 220 | results_directory=results_directory, |
| 221 | no_experimental=no_experimental) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 222 | else: |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 223 | logging.info('Scheduling test %s...', test) |
Aviv Keshet | 1071196 | 2013-06-24 12:20:33 -0700 | [diff] [blame] | 224 | ntests = schedule_local_test(autotest_path, test, afe, |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 225 | build=build, board=board, |
| 226 | results_directory=results_directory) |
Aviv Keshet | 69ebb6c | 2013-06-11 13:58:44 -0700 | [diff] [blame] | 227 | logging.info('... scheduled %s tests.', ntests) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 228 | |
Aviv Keshet | 5e33c17 | 2013-07-16 05:00:49 -0700 | [diff] [blame] | 229 | if not afe.get_jobs(): |
| 230 | logging.info('No jobs scheduled. End of local run.') |
Aviv Keshet | 5e33c17 | 2013-07-16 05:00:49 -0700 | [diff] [blame] | 231 | |
| 232 | last_job_id = afe.get_jobs()[-1].id |
| 233 | job_id_digits=len(str(last_job_id)) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 234 | for job in afe.get_jobs(): |
Aviv Keshet | 5e33c17 | 2013-07-16 05:00:49 -0700 | [diff] [blame] | 235 | run_job(job, remote, autotest_path, results_directory, fast_mode, |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 236 | job_id_digits, ssh_verbosity, args, pretend) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 237 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 238 | |
| 239 | def validate_arguments(arguments): |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 240 | """ |
| 241 | Validates parsed arguments. |
| 242 | |
| 243 | @param arguments: arguments object, as parsed by ParseArguments |
| 244 | @raises: ValueError if arguments were invalid. |
| 245 | """ |
Aviv Keshet | 30322f9 | 2013-07-18 13:21:52 -0700 | [diff] [blame] | 246 | if arguments.build: |
| 247 | raise ValueError('-i/--build flag not yet supported.') |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 248 | |
| 249 | if not arguments.board: |
| 250 | raise ValueError('Board autodetection not yet supported. ' |
| 251 | '--board required.') |
| 252 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 253 | if arguments.remote == ':lab:': |
| 254 | raise ValueError('Running tests in test lab not yet supported.') |
Aviv Keshet | 30322f9 | 2013-07-18 13:21:52 -0700 | [diff] [blame] | 255 | if arguments.args: |
| 256 | raise ValueError('--args flag not supported when running against ' |
| 257 | ':lab:') |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 258 | if arguments.pretend: |
| 259 | raise ValueError('--pretend flag not supported when running ' |
| 260 | 'against :lab:') |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 261 | |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 262 | if arguments.ssh_verbosity: |
| 263 | raise ValueError('--ssh_verbosity flag not supported when running ' |
| 264 | 'against :lab:') |
| 265 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 266 | |
| 267 | def parse_arguments(argv): |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 268 | """ |
| 269 | Parse command line arguments |
| 270 | |
| 271 | @param argv: argument list to parse |
| 272 | @returns: parsed arguments. |
| 273 | """ |
| 274 | parser = argparse.ArgumentParser(description='Run remote tests.') |
| 275 | |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 276 | parser.add_argument('remote', metavar='REMOTE', |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 277 | help='hostname[:port] for remote device. Specify ' |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 278 | ':lab: to run in test lab, or :vm:PORT_NUMBER to ' |
| 279 | 'run in vm.') |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 280 | parser.add_argument('tests', nargs='+', metavar='TEST', |
| 281 | help='Run given test(s). Use suite:SUITE to specify ' |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 282 | 'test suite.') |
Prathmesh Prabhu | 63f00aa | 2013-07-26 13:33:06 -0700 | [diff] [blame] | 283 | default_board = cros_build_lib.GetDefaultBoard() |
| 284 | parser.add_argument('-b', '--board', metavar='BOARD', default=default_board, |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 285 | action='store', |
Prathmesh Prabhu | 63f00aa | 2013-07-26 13:33:06 -0700 | [diff] [blame] | 286 | help='Board for which the test will run. Default: %s' % |
| 287 | (default_board or 'Not configured')) |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 288 | parser.add_argument('-i', '--build', metavar='BUILD', |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 289 | help='Build to test. Device will be reimaged if ' |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 290 | 'necessary. Omit flag to skip reimage and test ' |
| 291 | 'against already installed DUT image.') |
Christopher Wiley | f6b5aae | 2013-07-09 10:14:02 -0700 | [diff] [blame] | 292 | parser.add_argument('--fast', action='store_true', dest='fast_mode', |
| 293 | default=False, |
| 294 | help='Enable fast mode. This will cause test_that to ' |
| 295 | 'skip time consuming steps like sysinfo and ' |
| 296 | 'collecting crash information.') |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 297 | parser.add_argument('--args', metavar='ARGS', |
Aviv Keshet | 30322f9 | 2013-07-18 13:21:52 -0700 | [diff] [blame] | 298 | help='Argument string to pass through to test. Only ' |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 299 | 'supported for runs against a local DUT.') |
Aviv Keshet | 15782de | 2013-07-31 11:40:41 -0700 | [diff] [blame] | 300 | parser.add_argument('--results_dir', metavar='RESULTS_DIR', |
| 301 | help='Instead of storing results in a new subdirectory' |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 302 | ' of /tmp , store results in RESULTS_DIR. If ' |
| 303 | 'RESULTS_DIR already exists, will attempt to ' |
| 304 | 'continue using this directory, which may result ' |
| 305 | 'in test failures due to file collisions.') |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 306 | parser.add_argument('--pretend', action='store_true', default=False, |
| 307 | help='Print autoserv commands that would be run, ' |
| 308 | 'rather than running them.') |
Aviv Keshet | 8ea71df | 2013-07-19 10:49:36 -0700 | [diff] [blame] | 309 | parser.add_argument('--no-quickmerge', action='store_true', default=False, |
| 310 | dest='no_quickmerge', |
| 311 | help='Skip the quickmerge step and use the sysroot ' |
| 312 | 'as it currently is. May result in un-merged ' |
| 313 | 'source tree changes not being reflected in run.') |
Aviv Keshet | e9170d9 | 2013-07-19 11:20:45 -0700 | [diff] [blame] | 314 | parser.add_argument('--no-experimental', action='store_true', |
| 315 | default=False, dest='no_experimental', |
| 316 | help='When scheduling a suite, skip any tests marked ' |
| 317 | 'as experimental. Applies only to tests scheduled' |
| 318 | ' via suite:[SUITE].') |
Aviv Keshet | fd77591 | 2013-08-06 11:37:16 -0700 | [diff] [blame] | 319 | parser.add_argument('--whitelist-chrome-crashes', action='store_true', |
| 320 | default=False, dest='whitelist_chrome_crashes', |
| 321 | help='Ignore chrome crashes when producing test ' |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 322 | 'report. This flag gets passed along to the ' |
| 323 | 'report generation tool.') |
Aviv Keshet | c14951a | 2013-08-12 18:17:35 -0700 | [diff] [blame] | 324 | parser.add_argument('--ssh_verbosity', action='store', type=int, |
| 325 | choices=[0, 1, 2, 3], default=0, |
| 326 | help='Verbosity level for ssh, between 0 and 3 ' |
| 327 | 'inclusive.') |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 328 | parser.add_argument('--debug', action='store_true', |
| 329 | help='Include DEBUG level messages in stdout. Note: ' |
| 330 | 'these messages will be included in output log ' |
| 331 | 'file regardless.') |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 332 | return parser.parse_args(argv) |
| 333 | |
| 334 | |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 335 | def sigint_handler(signum, stack_frame): |
| 336 | #pylint: disable-msg=C0111 |
| 337 | """Handle SIGINT or SIGTERM to a local test_that run. |
| 338 | |
| 339 | This handler sends a SIGINT to the running autoserv process, |
| 340 | if one is running, giving it up to 5 seconds to clean up and exit. After |
| 341 | the timeout elapses, autoserv is killed. In either case, after autoserv |
| 342 | exits then this process exits with status 1. |
| 343 | """ |
| 344 | # If multiple signals arrive before handler is unset, ignore duplicates |
| 345 | if not _sigint_handler_lock.acquire(False): |
| 346 | return |
| 347 | try: |
| 348 | # Ignore future signals by unsetting handler. |
| 349 | signal.signal(signal.SIGINT, signal.SIG_IGN) |
| 350 | signal.signal(signal.SIGTERM, signal.SIG_IGN) |
| 351 | |
| 352 | logging.warning('Received SIGINT or SIGTERM. Cleaning up and exiting.') |
| 353 | if _autoserv_proc: |
| 354 | logging.warning('Sending SIGINT to autoserv process. Waiting up ' |
| 355 | 'to %s seconds for cleanup.', |
| 356 | _AUTOSERV_SIGINT_TIMEOUT_SECONDS) |
| 357 | _autoserv_proc.send_signal(signal.SIGINT) |
| 358 | timed_out, _ = retry.timeout(_autoserv_proc.wait, |
| 359 | timeout_sec=_AUTOSERV_SIGINT_TIMEOUT_SECONDS) |
| 360 | if timed_out: |
| 361 | _autoserv_proc.kill() |
| 362 | logging.warning('Timed out waiting for autoserv to handle ' |
| 363 | 'SIGINT. Killed autoserv.') |
| 364 | finally: |
| 365 | _sigint_handler_lock.release() # this is not really necessary? |
| 366 | sys.exit(1) |
| 367 | |
| 368 | |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 369 | def main(argv): |
| 370 | """ |
| 371 | Entry point for test_that script. |
| 372 | @param argv: arguments list |
| 373 | """ |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 374 | |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 375 | if not cros_build_lib.IsInsideChroot(): |
| 376 | print >> sys.stderr, 'Script must be invoked inside the chroot.' |
| 377 | return 1 |
Aviv Keshet | 712128f | 2013-06-11 14:41:08 -0700 | [diff] [blame] | 378 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 379 | arguments = parse_arguments(argv) |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 380 | try: |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 381 | validate_arguments(arguments) |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 382 | except ValueError as err: |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 383 | print >> sys.stderr, ('Invalid arguments. %s' % err.message) |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 384 | return 1 |
| 385 | |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 386 | sysroot_path = os.path.join('/build', arguments.board, '') |
| 387 | sysroot_autotest_path = os.path.join(sysroot_path, 'usr', 'local', |
| 388 | 'autotest', '') |
| 389 | sysroot_site_utils_path = os.path.join(sysroot_autotest_path, |
| 390 | 'site_utils') |
| 391 | |
| 392 | if not os.path.exists(sysroot_path): |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 393 | print >> sys.stderr, ('%s does not exist. Have you run ' |
| 394 | 'setup_board?' % sysroot_path) |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 395 | return 1 |
| 396 | if not os.path.exists(sysroot_autotest_path): |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 397 | print >> sys.stderr, ('%s does not exist. Have you run ' |
| 398 | 'build_packages?' % sysroot_autotest_path) |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 399 | return 1 |
| 400 | |
Aviv Keshet | 2dc9893 | 2013-06-17 15:22:10 -0700 | [diff] [blame] | 401 | # If we are not running the sysroot version of script, perform |
| 402 | # a quickmerge if necessary and then re-execute |
| 403 | # the sysroot version of script with the same arguments. |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 404 | realpath = os.path.realpath(__file__) |
| 405 | if os.path.dirname(realpath) != sysroot_site_utils_path: |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 406 | logging_manager.configure_logging( |
| 407 | server_logging_config.ServerLoggingConfig(), |
| 408 | use_console=True, |
| 409 | verbose=arguments.debug) |
Aviv Keshet | 8ea71df | 2013-07-19 10:49:36 -0700 | [diff] [blame] | 410 | if arguments.no_quickmerge: |
| 411 | logging.info('Skipping quickmerge step as requested.') |
| 412 | else: |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 413 | logging.info('Running autotest_quickmerge step.') |
| 414 | s = subprocess.Popen([_QUICKMERGE_SCRIPTNAME, |
| 415 | '--board='+arguments.board], |
| 416 | stdout=subprocess.PIPE, |
| 417 | stderr=subprocess.STDOUT) |
| 418 | for message in iter(s.stdout.readline, b''): |
| 419 | logging.debug('quickmerge| %s', message.strip()) |
| 420 | s.wait() |
Aviv Keshet | 2dc9893 | 2013-06-17 15:22:10 -0700 | [diff] [blame] | 421 | |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 422 | logging.info('Re-running test_that script in sysroot.') |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 423 | script_command = os.path.join(sysroot_site_utils_path, |
| 424 | os.path.basename(realpath)) |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 425 | proc = None |
| 426 | def resend_sig(signum, stack_frame): |
| 427 | #pylint: disable-msg=C0111 |
| 428 | if proc: |
| 429 | proc.send_signal(signum) |
| 430 | signal.signal(signal.SIGINT, resend_sig) |
| 431 | signal.signal(signal.SIGTERM, resend_sig) |
| 432 | |
| 433 | proc = subprocess.Popen([script_command] + argv) |
| 434 | |
| 435 | return proc.wait() |
Aviv Keshet | 0e5d525 | 2013-04-26 16:01:36 -0700 | [diff] [blame] | 436 | |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 437 | # We are running the sysroot version of the script. |
| 438 | # No further levels of bootstrapping that will occur, so |
| 439 | # create a results directory and start sending our logging messages |
| 440 | # to it. |
| 441 | results_directory = arguments.results_dir |
| 442 | if results_directory is None: |
| 443 | # Create a results_directory as subdir of /tmp |
| 444 | results_directory = tempfile.mkdtemp(prefix='test_that_results_') |
| 445 | else: |
| 446 | # Create results_directory if it does not exist |
| 447 | try: |
| 448 | os.makedirs(results_directory) |
| 449 | except OSError as e: |
| 450 | if e.errno != errno.EEXIST: |
| 451 | raise |
| 452 | |
| 453 | logging_manager.configure_logging( |
| 454 | server_logging_config.ServerLoggingConfig(), |
| 455 | results_dir=results_directory, |
| 456 | use_console=True, |
| 457 | verbose=arguments.debug, |
| 458 | debug_log_name='test_that') |
| 459 | logging.info('Began logging to %s', results_directory) |
| 460 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 461 | # Hard coded to True temporarily. This will eventually be parsed to false |
| 462 | # if we are doing a run in the test lab. |
| 463 | local_run = True |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 464 | |
Aviv Keshet | 1d991ea | 2013-06-12 17:24:23 -0700 | [diff] [blame] | 465 | signal.signal(signal.SIGINT, sigint_handler) |
| 466 | signal.signal(signal.SIGTERM, sigint_handler) |
| 467 | |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 468 | if local_run: |
| 469 | afe = setup_local_afe() |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 470 | perform_local_run(afe, sysroot_autotest_path, arguments.tests, |
| 471 | arguments.remote, arguments.fast_mode, |
| 472 | args=arguments.args, |
| 473 | pretend=arguments.pretend, |
| 474 | no_experimental=arguments.no_experimental, |
| 475 | results_directory=results_directory, |
| 476 | ssh_verbosity=arguments.ssh_verbosity) |
Aviv Keshet | c5e4609 | 2013-07-19 10:15:40 -0700 | [diff] [blame] | 477 | if arguments.pretend: |
| 478 | logging.info('Finished pretend run. Exiting.') |
| 479 | return 0 |
| 480 | |
Aviv Keshet | fd77591 | 2013-08-06 11:37:16 -0700 | [diff] [blame] | 481 | test_report_command = [_TEST_REPORT_SCRIPTNAME] |
| 482 | if arguments.whitelist_chrome_crashes: |
| 483 | test_report_command.append('--whitelist_chrome_crashes') |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 484 | test_report_command.append(results_directory) |
Aviv Keshet | fd77591 | 2013-08-06 11:37:16 -0700 | [diff] [blame] | 485 | final_result = subprocess.call(test_report_command) |
Prathmesh Prabhu | 0819a61 | 2013-07-18 15:50:02 -0700 | [diff] [blame] | 486 | logging.info('Finished running tests. Results can be found in %s', |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 487 | results_directory) |
Aviv Keshet | c9e7462 | 2013-07-18 10:11:11 -0700 | [diff] [blame] | 488 | try: |
| 489 | os.unlink(_LATEST_RESULTS_DIRECTORY) |
| 490 | except OSError: |
| 491 | pass |
Aviv Keshet | e43bccf | 2013-08-14 14:11:59 -0700 | [diff] [blame^] | 492 | os.symlink(results_directory, _LATEST_RESULTS_DIRECTORY) |
Prathmesh Prabhu | 0819a61 | 2013-07-18 15:50:02 -0700 | [diff] [blame] | 493 | return final_result |
Aviv Keshet | ba4992a | 2013-07-02 14:09:23 -0700 | [diff] [blame] | 494 | |
Aviv Keshet | 021c19f | 2013-02-22 13:19:43 -0800 | [diff] [blame] | 495 | |
| 496 | if __name__ == '__main__': |
Aviv Keshet | d4a0430 | 2013-04-30 15:48:30 -0700 | [diff] [blame] | 497 | sys.exit(main(sys.argv[1:])) |