[autotest] Add create_suite_job() site RPC.
Add create_suite_job() RPC to autotest using the site_rpc mechanism.
Also add support to atest to call this RPC.
The purpose of this RPC is to stage a build on the dev server (if
necessary), image N machines with it, and then run the desired
suite on those machines.
BUG=chromium-os:25573
TEST=atest suite create -b x86-mario -i x86-mario-release/R19-1675.0.0-a1-b1588 test_suites/control.dummy
Change-Id: I09288fe6ffc675e5b111f7f59e349015f52ebb8e
Reviewed-on: https://gerrit.chromium.org/gerrit/15279
Tested-by: Chris Masone <cmasone@chromium.org>
Commit-Ready: Scott Zawalski <scottz@chromium.org>
Reviewed-by: Scott Zawalski <scottz@chromium.org>
Tested-by: Scott Zawalski <scottz@chromium.org>
diff --git a/frontend/afe/site_rpc_interface.py b/frontend/afe/site_rpc_interface.py
new file mode 100644
index 0000000..6793645
--- /dev/null
+++ b/frontend/afe/site_rpc_interface.py
@@ -0,0 +1,72 @@
+# Copyright (c) 2012 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.
+
+__author__ = 'cmasone@chromium.org (Chris Masone)'
+
+import common
+import logging
+import sys
+from autotest_lib.client.common_lib import global_config
+from autotest_lib.client.common_lib.cros import dev_server
+# rpc_utils initializes django, which we can't do in unit tests.
+if 'unittest' not in sys.modules.keys():
+ # So, only load that module if we're not running unit tests.
+ from autotest_lib.frontend.afe import rpc_utils
+from autotest_lib.server.cros import control_file_getter, dynamic_suite
+
+
+class StageBuildFailure(Exception):
+ """Raised when the dev server throws 500 while staging a build."""
+ pass
+
+
+class ControlFileEmpty(Exception):
+ """Raised when the control file exists on the server, but can't be read."""
+ pass
+
+
+def _rpc_utils():
+ """Returns the rpc_utils module. MUST be mocked for unit tests."""
+ return rpc_utils
+
+
+def create_suite_job(suite_name, board, build):
+ """
+ Create a job to run a test suite on the given device with the given image.
+
+ When the timeout specified in the control file is reached, the
+ job is guaranteed to have completed and results will be available.
+
+ @param suite_name: the test suite to run, e.g. 'control.bvt'.
+ @param board: the kind of device to run the tests on.
+ @param build: unique name by which to refer to the image from now on.
+
+ @throws ControlFileNotFound if a unique suite control file doesn't exist.
+ @throws NoControlFileList if we can't list the control files at all.
+ @throws StageBuildFailure if the dev server throws 500 while staging build.
+ @throws ControlFileEmpty if the control file exists on the server, but
+ can't be read.
+
+ @return: the job ID of the suite; -1 on error.
+ """
+ # Ensure |build| is staged is on the dev server.
+ ds = dev_server.DevServer.create()
+ if not ds.trigger_download(build):
+ raise StageBuildFailure("Server error while staging " + build)
+
+ getter = control_file_getter.DevServerGetter.create(build, ds)
+ # Get the control file for the suite.
+ control_file_in = getter.get_control_file_contents_by_name(suite_name)
+ if not control_file_in:
+ raise ControlFileEmpty("Fetching %s returned no data." % suite_name)
+
+ # prepend build and board to the control file
+ control_file = dynamic_suite.inject_vars({'board': board, 'build': build},
+ control_file_in)
+
+ return _rpc_utils().create_job_common('%s-%s' % (build, suite_name),
+ priority='Medium',
+ control_type='Server',
+ control_file=control_file,
+ hostless=True)