[autotest] Be able to pass in args when creating job from the AFE.

A number of tests require args to run. I added a textbox which takes a list of
comma seperated args. The client side will split and trim the input and send
it to RPC. I added a optional argument "args" to the rpc interface
"create_job". To ensure the RPC interface backward compatible,
I added the argument at the end.

BUG=chromium:374918
DEPLOY=afe,apache
TEST=ran afe, create job and type in args

Change-Id: I59df10d4d03dd23181623afdda29c3a683d3685e
Reviewed-on: https://chromium-review.googlesource.com/203011
Tested-by: Jiaxi Luo <jiaxiluo@chromium.org>
Reviewed-by: Simran Basi <sbasi@chromium.org>
Commit-Queue: Jiaxi Luo <jiaxiluo@chromium.org>
diff --git a/frontend/afe/rpc_interface.py b/frontend/afe/rpc_interface.py
index 84c5fae..4812567 100644
--- a/frontend/afe/rpc_interface.py
+++ b/frontend/afe/rpc_interface.py
@@ -37,6 +37,7 @@
 from autotest_lib.frontend.afe import models, model_logic, model_attributes
 from autotest_lib.frontend.afe import control_file, rpc_utils
 from autotest_lib.frontend.afe import site_rpc_interface
+from autotest_lib.server.cros.dynamic_suite import tools
 
 def get_parameterized_autoupdate_image_url(job):
     """Get the parameterized autoupdate image url from a parameterized job."""
@@ -530,7 +531,8 @@
                run_verify=False, email_list='', dependencies=(),
                reboot_before=None, reboot_after=None, parse_failed_repair=None,
                hostless=False, keyvals=None, drone_set=None, image=None,
-               parent_job_id=None, test_retry=0, run_reset=True, **kwargs):
+               parent_job_id=None, test_retry=0, run_reset=True, args=(),
+               **kwargs):
     """\
     Create and enqueue a job.
 
@@ -539,12 +541,12 @@
     @param control_file String contents of the control file.
     @param control_type Type of control file, Client or Server.
     @param synch_count How many machines the job uses per autoserv execution.
-    synch_count == 1 means the job is asynchronous.  If an atomic group is
-    given this value is treated as a minimum.
+        synch_count == 1 means the job is asynchronous.  If an atomic group is
+        given this value is treated as a minimum.
     @param is_template If true then create a template job.
     @param timeout Hours after this call returns until the job times out.
     @param timeout_mins Minutes after this call returns until the job times
-    out.
+        out.
     @param max_runtime_mins Minutes from job starting time until job times out
     @param run_verify Should the host be verified before running the test?
     @param email_list String containing emails to mail when the job is done
@@ -552,24 +554,28 @@
     @param reboot_before Never, If dirty, or Always
     @param reboot_after Never, If all tests passed, or Always
     @param parse_failed_repair if true, results of failed repairs launched by
-    this job will be parsed as part of the job.
+        this job will be parsed as part of the job.
     @param hostless if true, create a hostless job
     @param keyvals dict of keyvals to associate with the job
     @param hosts List of hosts to run job on.
     @param meta_hosts List where each entry is a label name, and for each entry
-    one host will be chosen from that label to run the job on.
+        one host will be chosen from that label to run the job on.
     @param one_time_hosts List of hosts not in the database to run the job on.
     @param atomic_group_name The name of an atomic group to schedule the job on.
     @param drone_set The name of the drone set to run this test on.
     @param image OS image to install before running job.
     @param parent_job_id id of a job considered to be parent of created job.
     @param test_retry Number of times to retry test if the test did not
-                       complete successfully. (optional, default: 0)
+        complete successfully. (optional, default: 0)
     @param run_reset Should the host be reset before running the test?
+    @param args A list of args to be injected into control file.
     @param kwargs extra keyword args. NOT USED.
 
     @returns The created Job id number.
     """
+    if args:
+        control_file = tools.inject_vars({'args': args}, control_file)
+
     if image is None:
         return rpc_utils.create_job_common(
                 **rpc_utils.get_create_job_common_args(locals()))
diff --git a/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java b/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java
index a6d6423..6cf48b4 100644
--- a/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java
+++ b/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java
@@ -56,6 +56,7 @@
     private CheckBox parseFailedRepair = new CheckBox();
     private CheckBoxImpl hostless = new CheckBoxImpl();
     private TextBox pool = new TextBox();
+    private TextBoxImpl args = new TextBoxImpl();
     private TestSelectorDisplay testSelector = new TestSelectorDisplay();
     private CheckBoxPanelDisplay profilersPanel = new CheckBoxPanelDisplay(CHECKBOX_PANEL_COLUMNS);
     private CheckBoxImpl runNonProfiledIteration =
@@ -116,6 +117,7 @@
         panel.add(parseFailedRepair, "create_parse_failed_repair");
         panel.add(hostless, "create_hostless");
         panel.add(pool, "create_pool");
+        panel.add(args, "create_args");
         panel.add(testSelector, "create_tests");
         panel.add(profilerControls, "create_profilers");
         panel.add(controlFilePanel, "create_edit_control");
@@ -174,6 +176,10 @@
         return pool;
     }
 
+    public ITextBox getArgs() {
+        return args;
+    }
+
     public HasText getJobName() {
         return jobName;
     }
diff --git a/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java b/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
index 9a1139c..6154257 100644
--- a/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
+++ b/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
@@ -76,6 +76,7 @@
         public HasValue<Boolean> getParseFailedRepair();
         public ICheckBox getHostless();
         public HasText getPool();
+        public ITextBox getArgs();
         public HostSelector.Display getHostSelectorDisplay();
         public SimplifiedList getDroneSet();
         public ITextBox getSynchCountInput();
@@ -328,13 +329,12 @@
         for (JSONObject test : testSelector.getSelectedTests()) {
             tests.set(tests.size(), test.get("id"));
         }
+        params.put("tests", tests);
 
         JSONArray profilers = new JSONArray();
         for (ICheckBox profiler : profilersPanel.getChecked()) {
             profilers.set(profilers.size(), new JSONString(profiler.getText()));
         }
-
-        params.put("tests", tests);
         params.put("profilers", profilers);
 
         if (display.getRunNonProfiledIteration().isVisible()) {
@@ -615,6 +615,7 @@
         hostSelector.reset();
         dependencies = new JSONArray();
         display.getPool().setText("");
+        display.getArgs().setText("");
     }
 
     private void submitJob(final boolean isTemplate) {
@@ -686,6 +687,15 @@
                     args.put("image", new JSONString(imageUrlString));
                 }
 
+                String argsString = display.getArgs().getText().trim();
+                if (!argsString.equals("")) {
+                    JSONArray argsArray = new JSONArray();
+                    for (String arg : argsString.split(",")) {
+                        argsArray.set(argsArray.size(), new JSONString(arg.trim()));
+                    }
+                    args.put("args", argsArray);
+                }
+
                 rpcProxy.rpcCall("create_job_page_handler", args, new JsonRpcCallback() {
                     @Override
                     public void onSuccess(JSONValue result) {
diff --git a/frontend/client/src/autotest/public/AfeClient.html b/frontend/client/src/autotest/public/AfeClient.html
index 259891f..a93d5e1 100644
--- a/frontend/client/src/autotest/public/AfeClient.html
+++ b/frontend/client/src/autotest/public/AfeClient.html
@@ -151,6 +151,11 @@
               <td id="create_hostless"></td><td></td></tr>
           <tr><td class="field-name">Pool:</td>
               <td id="create_pool"></td><td></td></tr>
+          <tr><td class="field-name">Args:</td>
+              <td id="create_args"></td>
+              <td class="help">Example: "device_addrs=00:1F:20:33:6A:1E,
+              arg2=value2, arg3=value3" Separate multiple args with commas.
+              </td></tr>
 
           <tr id="create_drone_set_wrapper">
             <td class="field-name">Drone set:</td>