[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)