blob: 5983af5497a5af0cb92f0c0a9c5a6a7a40a9766d [file] [log] [blame]
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -07001# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Sequence extensions to server_job.
6Adds ability to schedule jobs on given machines.
7"""
8
Simran Basied9ee482015-09-29 15:17:26 -07009import logging
Simran Basia5522a32015-10-06 11:01:24 -070010import os
Simran Basied9ee482015-09-29 15:17:26 -070011
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070012import common
13from autotest_lib.client.common_lib import control_data
Simran Basied9ee482015-09-29 15:17:26 -070014from autotest_lib.server import utils
Simran Basia5522a32015-10-06 11:01:24 -070015from autotest_lib.server.cros.dynamic_suite import control_file_getter
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070016from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
17from autotest_lib.site_utils import job_directories
18
19MINUTE_IN_SECS = 60
20HOUR_IN_MINUTES = 60
21HOUR_IN_SECS = HOUR_IN_MINUTES * MINUTE_IN_SECS
22DAY_IN_HOURS = 24
23DAY_IN_SECS = DAY_IN_HOURS*HOUR_IN_SECS
24
25DEFAULT_JOB_TIMEOUT_IN_MINS = 4 * HOUR_IN_MINUTES
26
27class SequenceJob(object):
28 """Define part of a sequence that will be scheduled by the sequence test."""
29
30 CONTROL_FILE = """
31def run(machine):
Simran Basied9ee482015-09-29 15:17:26 -070032 job.run_test('%s', host=hosts.create_host(machine), client_ip=machine%s)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070033
34parallel_simple(run, machines)
35"""
36
Simran Basia5522a32015-10-06 11:01:24 -070037 def __init__(self, name, args=None, iteration=1, duration=None,
38 fetch_control_file=False):
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070039 """
40 Constructor
41
Simran Basia5522a32015-10-06 11:01:24 -070042 @param name: name of the server test to run.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070043 @param args: arguments needed by the server test.
44 @param iteration: number of copy of this test to sechudle
45 @param duration: expected duration of the test (in seconds).
Simran Basia5522a32015-10-06 11:01:24 -070046 @param fetch_control_file: If True, fetch the control file contents
47 from disk. Otherwise uses the template
48 control file.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070049 """
50 self._name = name
51 self._args = args or {}
52 self._iteration = iteration
53 self._duration = duration
Simran Basia5522a32015-10-06 11:01:24 -070054 self._fetch_control_file = fetch_control_file
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070055
56
57 def child_job_name(self, machine, iteration_number):
58 """
59 Return a name for a child job.
60
61 @param machine: machine name on which the test will run.
62 @param iteration_number: number with 0 and self._iteration - 1.
63
64 @returns a unique name based on the machine, the name and the iteration.
65 """
66 name_parts = [machine, self._name]
67 tag = self._args.get('tag')
68 if tag:
69 name_parts.append(tag)
70 if self._iteration > 1:
71 name_parts.append(str(iteration_number))
72 return '_'.join(name_parts)
73
74
75 def child_job_timeout(self):
76 """
77 Get the child job timeout in minutes.
78
79 @param args: arguments sent to the test.
80
81 @returns a timeout value for the test, 4h by default.
82 """
83 if self._duration:
84 return 2 * int(self._duration) / MINUTE_IN_SECS
85 # default value:
86 return DEFAULT_JOB_TIMEOUT_IN_MINS
87
88
89 def child_control_file(self):
90 """
Simran Basia5522a32015-10-06 11:01:24 -070091 Generate the child job's control file.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070092
Simran Basia5522a32015-10-06 11:01:24 -070093 If not fetching the contents, use the template control file and
94 populate the template control file with the test name and expand the
95 arguments list.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070096
97 @param test: name of the test to run
98 @param args: dictionary of argument for this test.
99
100 @returns a fully built control file to be use for the child job.
101 """
Simran Basia5522a32015-10-06 11:01:24 -0700102 if self._fetch_control_file:
103 # TODO (sbasi): Add arg support.
104 cntl_file_getter = control_file_getter.FileSystemGetter(
105 [os.path.join(os.path.dirname(os.path.realpath(__file__)),
106 '..')])
107 return cntl_file_getter.get_control_file_contents_by_name(
108 self._name)
Simran Basied9ee482015-09-29 15:17:26 -0700109 child_args = ['',]
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700110 for arg, value in self._args.iteritems():
111 child_args.append('%s=%s' % (arg, repr(value)))
112 if self._duration:
113 child_args.append('duration=%d' % self._duration)
114 return self.CONTROL_FILE % (self._name, ', '.join(child_args))
115
116
117 def schedule(self, job, timeout_mins, machine):
118 """
119 Sequence a job on the running AFE.
120
121 Will schedule a given test on the job machine(s).
122 Support a subset of tests:
123 - server job
124 - no hostless.
125 - no cleanup around tests.
126
127 @param job: server_job object that will server as parent.
128 @param timeout_mins: timeout to set up: if the test last more than
129 timeout_mins, the test will fail.
130 @param machine: machine to run the test on.
131
132 @returns a maximal time in minutes that the sequence can take.
133 """
134 afe = frontend_wrappers.RetryingAFE(timeout_min=30, delay_sec=10,
135 user=job.user, debug=False)
136 current_job_id = job_directories.get_job_id_or_task_id(job.resultdir)
Simran Basied9ee482015-09-29 15:17:26 -0700137 logging.debug('Current job id: %s', current_job_id)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700138 runtime_mins = self.child_job_timeout()
139
140 for i in xrange(0, self._iteration):
Simran Basied9ee482015-09-29 15:17:26 -0700141 child_job_name = self.child_job_name(machine, i)
142 logging.debug('Creating job: %s', child_job_name)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700143 afe.create_job(
144 self.child_control_file(),
Simran Basied9ee482015-09-29 15:17:26 -0700145 name=child_job_name,
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700146 priority='Medium',
147 control_type=control_data.CONTROL_TYPE.SERVER,
148 hosts=[machine], meta_hosts=(), one_time_hosts=(),
149 atomic_group_name=None, synch_count=None, is_template=False,
150 timeout_mins=timeout_mins + (i + 1) * runtime_mins,
151 max_runtime_mins=runtime_mins,
152 run_verify=False, email_list='', dependencies=(),
153 reboot_before=None, reboot_after=None,
154 parse_failed_repair=None,
155 hostless=False, keyvals=None,
156 drone_set=None, image=None,
157 parent_job_id=current_job_id, test_retry=0, run_reset=False,
Simran Basied9ee482015-09-29 15:17:26 -0700158 require_ssp=utils.is_in_container())
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700159 return runtime_mins * self._iteration
160
161
162def sequence_schedule(job, machines, server_tests):
163 """
164 Schedule the tests to run
165
166 Launch all the tests in the sequence on all machines.
167 Returns as soon as the jobs are launched.
168
169 @param job: Job running.
170 @param machines: machine to run on.
171 @param server_tests: Array of sequence_test objects.
172 """
173 for machine in machines:
174 timeout_mins = 0
175 for test in server_tests:
176 timeout_mins += test.schedule(job, timeout_mins, machine)