Autotest: Increase run timeout granularity

This CL modifies all references to max_runtime_hrs to instead be
max_runtime_mins. This includes the django models, rpc interfaces, the
cleanup timeout code, and the frontend java views.

The frontend java code will need to be recompiled once this commits to
prevent the frontend from breaking.

The cleanup timeout pathway has been adjusted to find all timedout jobs
by minute, and has been changed to run every 5 minutes vs every hour as
before.

BUG=chromium-os:36067
TEST=Ran on my local afe, ensure that jobs can still be created correctly,
     and jobs with short timeouts do indeed get aborted when expected.

Change-Id: Id7668bbd05a9b02c22e7c549fac232fae02fc728
Reviewed-on: https://gerrit.chromium.org/gerrit/37479
Reviewed-by: Scott Zawalski <scottz@chromium.org>
Commit-Ready: Simran Basi <sbasi@chromium.org>
Tested-by: Simran Basi <sbasi@chromium.org>
diff --git a/frontend/afe/doctests/001_rpc_test.txt b/frontend/afe/doctests/001_rpc_test.txt
index 39ab599..20b5fe7 100644
--- a/frontend/afe/doctests/001_rpc_test.txt
+++ b/frontend/afe/doctests/001_rpc_test.txt
@@ -538,7 +538,7 @@
 ...         'priority': 'Low',
 ...         'synch_count': 1,
 ...         'timeout': 72,
-...         'max_runtime_hrs': 72,
+...         'max_runtime_mins': 72*60,
 ...         'run_verify': 1,
 ...         'email_list': '',
 ...         'reboot_before': 'If dirty',
diff --git a/frontend/afe/models.py b/frontend/afe/models.py
index f560837..4193353 100644
--- a/frontend/afe/models.py
+++ b/frontend/afe/models.py
@@ -893,7 +893,9 @@
     synch_count: how many hosts should be used per autoserv execution
     run_verify: Whether or not to run the verify phase
     timeout: hours from queuing time until job times out
-    max_runtime_hrs: hours from job starting time until job times out
+    max_runtime_hrs: DEPRECATED - hours from job starting time until job
+                     times out
+    max_runtime_mins: minutes from job starting time until job times out
     email_list: list of people to email on completion delimited by any of:
                 white space, ',', ':', ';'
     dependency_labels: many-to-many relationship with labels corresponding to
@@ -906,8 +908,12 @@
     """
     DEFAULT_TIMEOUT = global_config.global_config.get_config_value(
         'AUTOTEST_WEB', 'job_timeout_default', default=240)
+    # MAX_RUNTIME_HRS is deprecated. Will be removed after switch to mins is
+    # completed.
     DEFAULT_MAX_RUNTIME_HRS = global_config.global_config.get_config_value(
         'AUTOTEST_WEB', 'job_max_runtime_hrs_default', default=72)
+    DEFAULT_MAX_RUNTIME_MINS = global_config.global_config.get_config_value(
+        'AUTOTEST_WEB', 'job_max_runtime_mins_default', default=72*60)
     DEFAULT_PARSE_FAILED_REPAIR = global_config.global_config.get_config_value(
         'AUTOTEST_WEB', 'parse_failed_repair_default', type=bool,
         default=False)
@@ -940,7 +946,10 @@
         default=DEFAULT_REBOOT_AFTER)
     parse_failed_repair = dbmodels.BooleanField(
         default=DEFAULT_PARSE_FAILED_REPAIR)
+    # max_runtime_hrs is deprecated. Will be removed after switch to mins is
+    # completed.
     max_runtime_hrs = dbmodels.IntegerField(default=DEFAULT_MAX_RUNTIME_HRS)
+    max_runtime_mins = dbmodels.IntegerField(default=DEFAULT_MAX_RUNTIME_MINS)
     drone_set = dbmodels.ForeignKey(DroneSet, null=True, blank=True)
 
     parameterized_job = dbmodels.ForeignKey(ParameterizedJob, null=True,
@@ -1018,7 +1027,7 @@
             control_type=options['control_type'],
             synch_count=options.get('synch_count'),
             timeout=options.get('timeout'),
-            max_runtime_hrs=options.get('max_runtime_hrs'),
+            max_runtime_mins=options.get('max_runtime_mins'),
             run_verify=options.get('run_verify'),
             email_list=options.get('email_list'),
             reboot_before=options.get('reboot_before'),
diff --git a/frontend/afe/resources.py b/frontend/afe/resources.py
index b783115..83a658b 100644
--- a/frontend/afe/resources.py
+++ b/frontend/afe/resources.py
@@ -436,7 +436,7 @@
             'machines_per_execution': 1,
             'run_verify': bool(_job_fields['run_verify'].default),
             'timeout_hrs': _job_fields['timeout'].default,
-            'maximum_runtime_hrs': _job_fields['max_runtime_hrs'].default,
+            'maximum_runtime_mins': _job_fields['max_runtime_mins'].default,
             'cleanup_before_job':
                 model_attributes.RebootBefore.get_string(
                     models.DEFAULT_REBOOT_BEFORE),
@@ -476,7 +476,7 @@
                 'machines_per_execution': job.synch_count,
                 'run_verify': bool(job.run_verify),
                 'timeout_hrs': job.timeout,
-                'maximum_runtime_hrs': job.max_runtime_hrs,
+                'maximum_runtime_mins': job.max_runtime_mins,
                 'cleanup_before_job':
                     model_attributes.RebootBefore.get_string(job.reboot_before),
                 'cleanup_after_job':
@@ -681,7 +681,7 @@
                 control_type=control_type,
                 is_template=input_dict.get('is_template', None),
                 timeout=execution_info.get('timeout_hrs'),
-                max_runtime_hrs=execution_info.get('maximum_runtime_hrs'),
+                max_runtime_mins=execution_info.get('maximum_runtime_mins'),
                 synch_count=execution_info.get('machines_per_execution'),
                 run_verify=execution_info.get('run_verify'),
                 email_list=input_dict.get('email_list', None),
diff --git a/frontend/afe/rpc_interface.py b/frontend/afe/rpc_interface.py
index 9c01d2c..2ddb72d 100644
--- a/frontend/afe/rpc_interface.py
+++ b/frontend/afe/rpc_interface.py
@@ -414,7 +414,7 @@
                              meta_hosts=(), one_time_hosts=(),
                              atomic_group_name=None, synch_count=None,
                              is_template=False, timeout=None,
-                             max_runtime_hrs=None, run_verify=True,
+                             max_runtime_mins=None, run_verify=True,
                              email_list='', dependencies=(), reboot_before=None,
                              reboot_after=None, parse_failed_repair=None,
                              hostless=False, keyvals=None, drone_set=None):
@@ -496,7 +496,7 @@
 def create_job(name, priority, control_file, control_type,
                hosts=(), meta_hosts=(), one_time_hosts=(),
                atomic_group_name=None, synch_count=None, is_template=False,
-               timeout=None, max_runtime_hrs=None, run_verify=True,
+               timeout=None, max_runtime_mins=None, run_verify=True,
                email_list='', dependencies=(), reboot_before=None,
                reboot_after=None, parse_failed_repair=None, hostless=False,
                keyvals=None, drone_set=None, image=None):
@@ -512,7 +512,7 @@
     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 max_runtime_hrs Hours from job starting time until job times 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
     @param dependencies List of label names on which this job depends
@@ -860,7 +860,8 @@
     result['host_statuses'] = sorted(models.Host.Status.names)
     result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
     result['job_timeout_default'] = models.Job.DEFAULT_TIMEOUT
-    result['job_max_runtime_hrs_default'] = models.Job.DEFAULT_MAX_RUNTIME_HRS
+    result['job_max_runtime_mins_default'] = (
+        models.Job.DEFAULT_MAX_RUNTIME_MINS)
     result['parse_failed_repair_default'] = bool(
         models.Job.DEFAULT_PARSE_FAILED_REPAIR)
     result['reboot_before_options'] = model_attributes.RebootBefore.names
diff --git a/frontend/afe/rpc_utils.py b/frontend/afe/rpc_utils.py
index 7250d92..a2781bd 100644
--- a/frontend/afe/rpc_utils.py
+++ b/frontend/afe/rpc_utils.py
@@ -638,7 +638,7 @@
 def create_job_common(name, priority, control_type, control_file=None,
                       hosts=(), meta_hosts=(), one_time_hosts=(),
                       atomic_group_name=None, synch_count=None,
-                      is_template=False, timeout=None, max_runtime_hrs=None,
+                      is_template=False, timeout=None, max_runtime_mins=None,
                       run_verify=True, email_list='', dependencies=(),
                       reboot_before=None, reboot_after=None,
                       parse_failed_repair=None, hostless=False, keyvals=None,
@@ -735,7 +735,7 @@
                    control_type=control_type,
                    is_template=is_template,
                    timeout=timeout,
-                   max_runtime_hrs=max_runtime_hrs,
+                   max_runtime_mins=max_runtime_mins,
                    synch_count=synch_count,
                    run_verify=run_verify,
                    email_list=email_list,
diff --git a/frontend/client/src/autotest/afe/AfeUtils.java b/frontend/client/src/autotest/afe/AfeUtils.java
index f435a0a..e78b0af 100644
--- a/frontend/client/src/autotest/afe/AfeUtils.java
+++ b/frontend/client/src/autotest/afe/AfeUtils.java
@@ -218,7 +218,7 @@
         args.put("control_type", new JSONString(TestSelector.SERVER_TYPE));
         args.put("synch_count", controlInfo.get("synch_count"));
         args.put("timeout", staticData.getData("job_timeout_default"));
-        args.put("max_runtime_hrs", staticData.getData("job_max_runtime_hrs_default"));
+        args.put("max_runtime_mins", staticData.getData("job_max_runtime_mins_default"));
         args.put("run_verify", JSONBoolean.getInstance(false));
         args.put("parse_failed_repair", JSONBoolean.getInstance(true));
         args.put("reboot_before", rebootBefore);
diff --git a/frontend/client/src/autotest/afe/JobDetailView.java b/frontend/client/src/autotest/afe/JobDetailView.java
index 36bc5ff..362de44 100644
--- a/frontend/client/src/autotest/afe/JobDetailView.java
+++ b/frontend/client/src/autotest/afe/JobDetailView.java
@@ -129,7 +129,7 @@
                     imageUrlString = Utils.jsonToString(jobObject.get("image")).trim();
                 }
                 showText(imageUrlString, "view_image_url");
-                showField(jobObject, "max_runtime_hrs", "view_max_runtime");
+                showField(jobObject, "max_runtime_mins", "view_max_runtime");
                 showField(jobObject, "email_list", "view_email_list");
                 showText(runVerify, "view_run_verify");
                 showField(jobObject, "reboot_before", "view_reboot_before");
diff --git a/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java b/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
index 3c9c476..e0cf25c 100644
--- a/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
+++ b/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
@@ -144,7 +144,7 @@
         display.getPriorityList().selectByName(priority);
 
         display.getTimeout().setText(Utils.jsonToString(jobObject.get("timeout")));
-        display.getMaxRuntime().setText(Utils.jsonToString(jobObject.get("max_runtime_hrs")));
+        display.getMaxRuntime().setText(Utils.jsonToString(jobObject.get("max_runtime_mins")));
         display.getEmailList().setText(
                 jobObject.get("email_list").isString().stringValue());
 
@@ -527,7 +527,7 @@
         display.getImageUrl().setText("");
         display.getTimeout().setText(Utils.jsonToString(repository.getData("job_timeout_default")));
         display.getMaxRuntime().setText(
-                Utils.jsonToString(repository.getData("job_max_runtime_hrs_default")));
+                Utils.jsonToString(repository.getData("job_max_runtime_mins_default")));
         display.getEmailList().setText("");
         testSelector.reset();
         display.getSkipVerify().setValue(false);
@@ -579,7 +579,7 @@
                          new JSONString(controlTypeSelect.getControlType()));
                 args.put("synch_count", synchCount);
                 args.put("timeout", new JSONNumber(timeoutValue));
-                args.put("max_runtime_hrs", new JSONNumber(maxRuntimeValue));
+                args.put("max_runtime_mins", new JSONNumber(maxRuntimeValue));
                 args.put("email_list", new JSONString(display.getEmailList().getText()));
                 args.put("run_verify", JSONBoolean.getInstance(
                         !display.getSkipVerify().getValue()));
diff --git a/frontend/client/src/autotest/public/AfeClient.html b/frontend/client/src/autotest/public/AfeClient.html
index d1cb538..b3a3e53 100644
--- a/frontend/client/src/autotest/public/AfeClient.html
+++ b/frontend/client/src/autotest/public/AfeClient.html
@@ -63,7 +63,7 @@
           <span class="field-name" style="color:red">Image URL:</span>
           <span id="view_image_url"></span><br>
           <span class="field-name">Max runtime:</span>
-          <span id="view_max_runtime"></span> hours<br>
+          <span id="view_max_runtime"></span> minutes<br>
           <span class="field-name">Email List:</span>
           <span id="view_email_list"></span><br>
           <span class="field-name">Run verify:</span>
@@ -125,7 +125,7 @@
               <td id="create_image_url"></td><td></td></tr>
           <tr><td class="field-name">Timeout (hours):</td>
               <td id="create_timeout"></td><td></td></tr>
-          <tr><td class="field-name">Max runtime (hours):</td>
+          <tr><td class="field-name">Max runtime (minutes):</td>
               <td id="create_max_runtime"></td><td></td></tr>
           <tr><td class="field-name">Email List:</td>
               <td id="create_email_list"></td><td></td></tr>