autotest: Support paygen tests in trampoline.
BUG=chromium:951872
TEST=site_utils/run_suite.py --board=coral --build coral-release/R75-12074.0.0 --model=astronaut --suite_name paygen_au_dev --file_bugs True --pool bvt --no_wait True --priority CQ --timeout_mins 600 --suite_min_duts 3
Change-Id: I917f7331a7d2983f4acab310d57928808eaf57b2
Reviewed-on: https://chromium-review.googlesource.com/1565284
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Xixuan Wu <xixuan@chromium.org>
Reviewed-by: Xixuan Wu <xixuan@chromium.org>
diff --git a/client/common_lib/control_data.py b/client/common_lib/control_data.py
index 94c78e9..517dd10 100644
--- a/client/common_lib/control_data.py
+++ b/client/common_lib/control_data.py
@@ -293,6 +293,24 @@
def set_fast(self, val):
self._set_bool('fast', val)
+ def set_update_type(self, val):
+ self._set_string('update_type', val)
+
+ def set_source_release(self, val):
+ self._set_string('source_release', val)
+
+ def set_target_release(self, val):
+ self._set_string('target_release', val)
+
+ def set_target_payload_uri(self, val):
+ self._set_string('target_payload_uri', val)
+
+ def set_source_payload_uri(self, val):
+ self._set_string('source_payload_uri', val)
+
+ def set_source_archive_uri(self, val):
+ self._set_string('source_archive_uri', val)
+
def set_attributes(self, val):
# Add subsystem:default if subsystem is not specified.
self._set_set('attributes', val)
diff --git a/server/cros/dynamic_suite/suite_common.py b/server/cros/dynamic_suite/suite_common.py
index 1430039..2e2fd51 100644
--- a/server/cros/dynamic_suite/suite_common.py
+++ b/server/cros/dynamic_suite/suite_common.py
@@ -109,13 +109,14 @@
return test_source_build
-def stage_build_artifacts(build, hostname=None):
+def stage_build_artifacts(build, hostname=None, artifacts=[]):
"""
Ensure components of |build| necessary for installing images are staged.
@param build image we want to stage.
@param hostname hostname of a dut may run test on. This is to help to locate
a devserver closer to duts if needed. Default is None.
+ @param artifacts A list of string artifact name to be staged.
@raises StageControlFileFailure: if the dev server throws 500 while staging
suite control files.
@@ -132,6 +133,8 @@
timings[constants.DOWNLOAD_STARTED_TIME] = _formatted_now()
try:
ds.stage_artifacts(image=build, artifacts=['test_suites'])
+ if artifacts:
+ ds.stage_artifacts(image=build, artifacts=artifacts)
except dev_server.DevServerException as e:
raise error.StageControlFileFailure(
"Failed to stage %s on %s: %s" % (build, ds_name, e))
diff --git a/site_utils/paygen.py b/site_utils/paygen.py
new file mode 100644
index 0000000..ba5b0f6
--- /dev/null
+++ b/site_utils/paygen.py
@@ -0,0 +1,81 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This is a temporary module to help scheduling paygen suites in trampoline.
+
+In trampoline, paygen suites are scheduled via skylab create-test, to schedule
+every paygen test independently, instead of creating a paygen suite via
+skylab create-suite.
+"""
+
+import re
+
+import common
+
+from autotest_lib.server.cros.dynamic_suite import control_file_getter
+from autotest_lib.server.cros.dynamic_suite import suite_common
+
+
+def is_paygen_suite(suite_name):
+ """Check if it's to run a paygen suite in trampoline."""
+ paygen_au_regexp = 'paygen_au_*'
+ return re.match(paygen_au_regexp, suite_name) is not None
+
+
+def get_paygen_tests(build, suite_name):
+ """Parse paygen tests from au control files."""
+ if not is_paygen_suite(suite_name):
+ raise ValueError('Cannot download paygen test control files for '
+ 'non-paygen suite %s' % suite_name)
+
+ ds, _ = suite_common.stage_build_artifacts(
+ build, artifacts=['%s_suite' % suite_name])
+ cf_getter = control_file_getter.DevServerGetter(build, ds)
+ tests = suite_common.retrieve_for_suite(cf_getter, suite_name)
+ return suite_common.filter_tests(
+ tests, suite_common.name_in_tag_predicate(suite_name))
+
+
+def paygen_skylab_args(test, suite_name, image, pool, board, model,
+ timeout_mins, qs_account, service_account):
+ """Form args for requesting paygen tests in skylab."""
+ args = ['-image', image]
+ args += ['-pool', pool]
+ if board is not None:
+ args += ['-board', board]
+
+ if model is not None:
+ args += ['-model', model]
+
+ args += ['-timeout-mins', str(timeout_mins)]
+
+ tags = ['skylab:run_suite_trampoline',
+ 'build:%s' % image,
+ 'suite:%s' % suite_name]
+ for t in tags:
+ args += ['-tag', t]
+
+ keyvals = ['build:%s' % image,
+ 'suite:%s' % suite_name,
+ 'label:%s/%s/%s' % (image, suite_name, test.name)]
+ for k in keyvals:
+ args += ['-keyval', k]
+
+ # Paygen test expects a space-separated string of name=value pairs.
+ # See http://shortn/_C8r3rC0rOP.
+ test_args = ['name=%s' % test.suite,
+ 'update_type=%s' % test.update_type,
+ 'source_release=%s' % test.source_release,
+ 'target_release=%s' % test.target_release,
+ 'target_payload_uri=%s' % test.target_payload_uri,
+ 'source_payload_uri=%s' % test.source_payload_uri,
+ 'suite=%s' % test.suite,
+ 'source_archive_uri=%s' % test.source_archive_uri]
+ args += ['-test-args', ' '.join(test_args)]
+
+ if qs_account:
+ args += ['-qs-account', qs_account]
+ args += ['-service-account-json', service_account]
+
+ return args + ['autoupdate_EndToEndTest']
diff --git a/site_utils/run_suite.py b/site_utils/run_suite.py
index ec90fb4..4d2a6f7 100755
--- a/site_utils/run_suite.py
+++ b/site_utils/run_suite.py
@@ -96,6 +96,8 @@
'Please re-run utils/build_externals.py inside[outside] '
'of the chroot accordingly.')
raise
+
+from autotest_lib.site_utils import paygen
from autotest_lib.site_utils import run_suite_common
CONFIG = global_config.global_config
@@ -2141,8 +2143,44 @@
logging.info(stdout)
+def _run_paygen_with_skylab(options, override_pool, override_qs_account):
+ """Run paygen suites with skylab."""
+ builds = suite_common.make_builds_from_options(options)
+ skylab_tool = os.environ.get('SKYLAB_TOOL') or _SKYLAB_TOOL
+ test_source_build = suite_common.get_test_source_build(builds)
+ pool = ('DUT_POOL_%s' % options.pool.upper()
+ if not override_pool else override_pool)
+ paygen_tests = paygen.get_paygen_tests(test_source_build, options.name)
+ for test in paygen_tests:
+ cmd = [skylab_tool, 'create-test']
+ cmd += paygen.paygen_skylab_args(
+ test, options.name, test_source_build, pool, options.board,
+ options.model, options.timeout_mins,
+ override_qs_account, _SKYLAB_SERVICE_ACCOUNT)
+ job_created_on = time.time()
+ try:
+ res = cros_build_lib.RunCommand(cmd, capture_output=True)
+ except cros_build_lib.RunCommandError as e:
+ logging.error(str(e))
+ return run_suite_common.SuiteResult(
+ run_suite_common.RETURN_CODES.INFRA_FAILURE)
+
+ logging.info(res.output)
+ job_url = res.output.split()[-1]
+ job_id = job_url.split('id=')[-1]
+ job_timer = diagnosis_utils.JobTimer(
+ job_created_on, float(options.timeout_mins))
+ _log_create_task(job_timer, job_url, job_id)
+
+ return run_suite_common.SuiteResult(run_suite_common.RETURN_CODES.OK)
+
+
def _run_with_skylab(options, override_pool, override_qs_account):
"""Run suite inside skylab."""
+ if paygen.is_paygen_suite(options.name):
+ return _run_paygen_with_skylab(options, override_pool,
+ override_qs_account)
+
builds = suite_common.make_builds_from_options(options)
skylab_tool = os.environ.get('SKYLAB_TOOL') or _SKYLAB_TOOL
pool = override_pool or options.pool