Dan Shi | 4df3925 | 2013-03-19 13:19:45 -0700 | [diff] [blame] | 1 | # pylint: disable-msg=C0111 |
| 2 | |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 3 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
| 6 | |
| 7 | __author__ = 'cmasone@chromium.org (Chris Masone)' |
| 8 | |
| 9 | import common |
Chris Masone | a8066a9 | 2012-05-01 16:52:31 -0700 | [diff] [blame] | 10 | import datetime |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 11 | import logging |
Aviv Keshet | d83ef44 | 2013-01-16 16:19:35 -0800 | [diff] [blame] | 12 | |
| 13 | from autotest_lib.client.common_lib import error |
Alex Miller | 7d658cf | 2013-09-04 16:00:35 -0700 | [diff] [blame] | 14 | from autotest_lib.client.common_lib import priorities |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 15 | from autotest_lib.client.common_lib.cros import dev_server |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 16 | from autotest_lib.server.cros.dynamic_suite import constants |
Chris Masone | b493555 | 2012-08-14 12:05:54 -0700 | [diff] [blame] | 17 | from autotest_lib.server.cros.dynamic_suite import control_file_getter |
Chris Masone | b493555 | 2012-08-14 12:05:54 -0700 | [diff] [blame] | 18 | from autotest_lib.server.cros.dynamic_suite import job_status |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 19 | from autotest_lib.server.cros.dynamic_suite import tools |
Dan Shi | 4df3925 | 2013-03-19 13:19:45 -0700 | [diff] [blame] | 20 | from autotest_lib.site_utils.graphite import stats |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 21 | |
| 22 | |
Chris Masone | f8b5306 | 2012-05-08 22:14:18 -0700 | [diff] [blame] | 23 | # Relevant CrosDynamicSuiteExceptions are defined in client/common_lib/error.py. |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 24 | |
| 25 | |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 26 | def _rpc_utils(): |
Chris Masone | b1451a0 | 2012-03-12 10:21:14 -0700 | [diff] [blame] | 27 | """Returns the rpc_utils module. MUST be mocked for unit tests. |
| 28 | |
| 29 | rpc_utils initializes django, which we can't do in unit tests. |
| 30 | This layer of indirection allows us to only load that module if we're |
| 31 | not running unit tests. |
| 32 | |
| 33 | @return: autotest_lib.frontend.afe.rpc_utils |
| 34 | """ |
| 35 | from autotest_lib.frontend.afe import rpc_utils |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 36 | return rpc_utils |
| 37 | |
| 38 | |
Chris Masone | 6257912 | 2012-03-08 15:18:43 -0800 | [diff] [blame] | 39 | def canonicalize_suite_name(suite_name): |
| 40 | return 'test_suites/control.%s' % suite_name |
| 41 | |
| 42 | |
Chris Masone | aa10f8e | 2012-05-15 13:34:21 -0700 | [diff] [blame] | 43 | def formatted_now(): |
Chris Masone | 8d6e641 | 2012-06-28 11:20:56 -0700 | [diff] [blame] | 44 | return datetime.datetime.now().strftime(job_status.TIME_FMT) |
Chris Masone | aa10f8e | 2012-05-15 13:34:21 -0700 | [diff] [blame] | 45 | |
| 46 | |
Chris Masone | 8dd27e0 | 2012-06-25 15:59:43 -0700 | [diff] [blame] | 47 | def get_control_file_contents_by_name(build, board, ds, suite_name): |
| 48 | """Return control file contents for |suite_name|. |
| 49 | |
| 50 | Query the dev server at |ds| for the control file |suite_name|, included |
| 51 | in |build| for |board|. |
| 52 | |
| 53 | @param build: unique name by which to refer to the image from now on. |
| 54 | @param board: the kind of device to run the tests on. |
| 55 | @param ds: a dev_server.DevServer instance to fetch control file with. |
| 56 | @param suite_name: canonicalized suite name, e.g. test_suites/control.bvt. |
| 57 | @raises ControlFileNotFound if a unique suite control file doesn't exist. |
| 58 | @raises NoControlFileList if we can't list the control files at all. |
| 59 | @raises ControlFileEmpty if the control file exists on the server, but |
| 60 | can't be read. |
| 61 | |
| 62 | @return the contents of the desired control file. |
| 63 | """ |
| 64 | getter = control_file_getter.DevServerGetter.create(build, ds) |
| 65 | # Get the control file for the suite. |
| 66 | try: |
| 67 | control_file_in = getter.get_control_file_contents_by_name(suite_name) |
| 68 | except error.CrosDynamicSuiteException as e: |
| 69 | raise type(e)("%s while testing %s for %s." % (e, build, board)) |
| 70 | if not control_file_in: |
| 71 | raise error.ControlFileEmpty( |
| 72 | "Fetching %s returned no data." % suite_name) |
Alex Miller | a713e25 | 2013-03-01 10:45:44 -0800 | [diff] [blame] | 73 | # Force control files to only contain ascii characters. |
| 74 | try: |
| 75 | control_file_in.encode('ascii') |
| 76 | except UnicodeDecodeError as e: |
| 77 | raise error.ControlFileMalformed(str(e)) |
| 78 | |
Chris Masone | 8dd27e0 | 2012-06-25 15:59:43 -0700 | [diff] [blame] | 79 | return control_file_in |
| 80 | |
| 81 | |
Chris Masone | 46d0eb1 | 2012-07-27 18:56:39 -0700 | [diff] [blame] | 82 | def create_suite_job(suite_name, board, build, pool, check_hosts=True, |
Alex Miller | 139690b | 2013-09-07 15:35:49 -0700 | [diff] [blame] | 83 | num=None, file_bugs=False, timeout=24, |
Aviv Keshet | 7cd1231 | 2013-07-25 10:25:55 -0700 | [diff] [blame^] | 84 | priority=priorities.Priority.DEFAULT, |
| 85 | suite_args=None): |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 86 | """ |
| 87 | Create a job to run a test suite on the given device with the given image. |
| 88 | |
| 89 | When the timeout specified in the control file is reached, the |
| 90 | job is guaranteed to have completed and results will be available. |
| 91 | |
Scott Zawalski | 6565017 | 2012-02-16 11:48:26 -0500 | [diff] [blame] | 92 | @param suite_name: the test suite to run, e.g. 'bvt'. |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 93 | @param board: the kind of device to run the tests on. |
| 94 | @param build: unique name by which to refer to the image from now on. |
Scott Zawalski | 6565017 | 2012-02-16 11:48:26 -0500 | [diff] [blame] | 95 | @param pool: Specify the pool of machines to use for scheduling |
| 96 | purposes. |
Chris Masone | 6257912 | 2012-03-08 15:18:43 -0800 | [diff] [blame] | 97 | @param check_hosts: require appropriate live hosts to exist in the lab. |
Aviv Keshet | d83ef44 | 2013-01-16 16:19:35 -0800 | [diff] [blame] | 98 | @param num: Specify the number of machines to schedule across (integer). |
| 99 | Leave unspecified or use None to use default sharding factor. |
Alex Miller | c577f3e | 2012-09-27 14:06:07 -0700 | [diff] [blame] | 100 | @param file_bugs: File a bug on each test failure in this suite. |
Alex Miller | 139690b | 2013-09-07 15:35:49 -0700 | [diff] [blame] | 101 | @param timeout: The max lifetime of this suite, in hours. |
| 102 | @param priority: Integer denoting priority. Higher is more important. |
Aviv Keshet | 7cd1231 | 2013-07-25 10:25:55 -0700 | [diff] [blame^] | 103 | @param suite_args: Optional arguments which will be parsed by the suite |
| 104 | control file. Used by control.test_that_wrapper to |
| 105 | determine which tests to run. |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 106 | |
Chris Masone | 8dd27e0 | 2012-06-25 15:59:43 -0700 | [diff] [blame] | 107 | @raises ControlFileNotFound: if a unique suite control file doesn't exist. |
| 108 | @raises NoControlFileList: if we can't list the control files at all. |
| 109 | @raises StageBuildFailure: if the dev server throws 500 while staging build. |
| 110 | @raises ControlFileEmpty: if the control file exists on the server, but |
| 111 | can't be read. |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 112 | |
| 113 | @return: the job ID of the suite; -1 on error. |
| 114 | """ |
Dan Shi | 4df3925 | 2013-03-19 13:19:45 -0700 | [diff] [blame] | 115 | stats.Counter('create_suite_job').increment() |
Scott Zawalski | 6565017 | 2012-02-16 11:48:26 -0500 | [diff] [blame] | 116 | # All suite names are assumed under test_suites/control.XX. |
Chris Masone | 6257912 | 2012-03-08 15:18:43 -0800 | [diff] [blame] | 117 | suite_name = canonicalize_suite_name(suite_name) |
Aviv Keshet | d83ef44 | 2013-01-16 16:19:35 -0800 | [diff] [blame] | 118 | if type(num) is not int and num is not None: |
Chris Sosa | 18c70b3 | 2013-02-15 14:12:43 -0800 | [diff] [blame] | 119 | raise error.SuiteArgumentException('Ill specified num argument %r. ' |
| 120 | 'Must be an integer or None.' % num) |
Aviv Keshet | d83ef44 | 2013-01-16 16:19:35 -0800 | [diff] [blame] | 121 | if num == 0: |
| 122 | logging.warning("Can't run on 0 hosts; using default.") |
| 123 | num = None |
Chris Masone | a8066a9 | 2012-05-01 16:52:31 -0700 | [diff] [blame] | 124 | |
| 125 | timings = {} |
Chris Sosa | 6b288c8 | 2012-03-29 15:31:06 -0700 | [diff] [blame] | 126 | # Ensure components of |build| necessary for installing images are staged |
| 127 | # on the dev server. However set synchronous to False to allow other |
| 128 | # components to be downloaded in the background. |
Chris Sosa | accb5ce | 2012-08-30 17:29:15 -0700 | [diff] [blame] | 129 | ds = dev_server.ImageServer.resolve(build) |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 130 | timings[constants.DOWNLOAD_STARTED_TIME] = formatted_now() |
Chris Masone | f70650c | 2012-05-16 08:52:12 -0700 | [diff] [blame] | 131 | try: |
| 132 | ds.trigger_download(build, synchronous=False) |
| 133 | except dev_server.DevServerException as e: |
Chris Masone | 8dd27e0 | 2012-06-25 15:59:43 -0700 | [diff] [blame] | 134 | raise error.StageBuildFailure( |
| 135 | "Failed to stage %s for %s: %s" % (build, board, e)) |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 136 | timings[constants.PAYLOAD_FINISHED_TIME] = formatted_now() |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 137 | |
Chris Masone | 8dd27e0 | 2012-06-25 15:59:43 -0700 | [diff] [blame] | 138 | control_file_in = get_control_file_contents_by_name(build, board, ds, |
| 139 | suite_name) |
Chris Masone | 46d0eb1 | 2012-07-27 18:56:39 -0700 | [diff] [blame] | 140 | |
| 141 | |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 142 | # prepend build and board to the control file |
Scott Zawalski | 6565017 | 2012-02-16 11:48:26 -0500 | [diff] [blame] | 143 | inject_dict = {'board': board, |
| 144 | 'build': build, |
Chris Masone | 6257912 | 2012-03-08 15:18:43 -0800 | [diff] [blame] | 145 | 'check_hosts': check_hosts, |
Chris Masone | 46d0eb1 | 2012-07-27 18:56:39 -0700 | [diff] [blame] | 146 | 'pool': pool, |
Aviv Keshet | d83ef44 | 2013-01-16 16:19:35 -0800 | [diff] [blame] | 147 | 'num': num, |
Dan Shi | b8a9911 | 2013-06-18 13:46:10 -0700 | [diff] [blame] | 148 | 'file_bugs': file_bugs, |
Alex Miller | 139690b | 2013-09-07 15:35:49 -0700 | [diff] [blame] | 149 | 'timeout': timeout, |
Alex Miller | 7d658cf | 2013-09-04 16:00:35 -0700 | [diff] [blame] | 150 | 'devserver_url': ds.url(), |
Aviv Keshet | 7cd1231 | 2013-07-25 10:25:55 -0700 | [diff] [blame^] | 151 | 'priority': priority, |
| 152 | 'suite_args' : suite_args |
| 153 | } |
| 154 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 155 | control_file = tools.inject_vars(inject_dict, control_file_in) |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 156 | |
| 157 | return _rpc_utils().create_job_common('%s-%s' % (build, suite_name), |
Alex Miller | 7d658cf | 2013-09-04 16:00:35 -0700 | [diff] [blame] | 158 | priority=priority, |
Alex Miller | 139690b | 2013-09-07 15:35:49 -0700 | [diff] [blame] | 159 | timeout=timeout, |
| 160 | max_runtime_mins=timeout*60, |
Chris Masone | 859fdec | 2012-01-30 08:38:09 -0800 | [diff] [blame] | 161 | control_type='Server', |
| 162 | control_file=control_file, |
Chris Masone | a8066a9 | 2012-05-01 16:52:31 -0700 | [diff] [blame] | 163 | hostless=True, |
| 164 | keyvals=timings) |