blob: 3a86f58f1e6fe1139b0c944c4e9d8ce219eb249c [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()
Simran Basi527c55f2015-12-14 11:57:17 -0800139 hostname = utils.get_hostname_from_machine(machine)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700140
141 for i in xrange(0, self._iteration):
Simran Basi527c55f2015-12-14 11:57:17 -0800142 child_job_name = self.child_job_name(hostname, i)
Simran Basied9ee482015-09-29 15:17:26 -0700143 logging.debug('Creating job: %s', child_job_name)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700144 afe.create_job(
145 self.child_control_file(),
Simran Basied9ee482015-09-29 15:17:26 -0700146 name=child_job_name,
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700147 priority='Medium',
148 control_type=control_data.CONTROL_TYPE.SERVER,
Simran Basi527c55f2015-12-14 11:57:17 -0800149 hosts=[hostname], meta_hosts=(), one_time_hosts=(),
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700150 atomic_group_name=None, synch_count=None, is_template=False,
151 timeout_mins=timeout_mins + (i + 1) * runtime_mins,
152 max_runtime_mins=runtime_mins,
153 run_verify=False, email_list='', dependencies=(),
154 reboot_before=None, reboot_after=None,
155 parse_failed_repair=None,
156 hostless=False, keyvals=None,
157 drone_set=None, image=None,
158 parent_job_id=current_job_id, test_retry=0, run_reset=False,
Simran Basied9ee482015-09-29 15:17:26 -0700159 require_ssp=utils.is_in_container())
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700160 return runtime_mins * self._iteration
161
162
163def sequence_schedule(job, machines, server_tests):
164 """
165 Schedule the tests to run
166
167 Launch all the tests in the sequence on all machines.
168 Returns as soon as the jobs are launched.
169
170 @param job: Job running.
171 @param machines: machine to run on.
172 @param server_tests: Array of sequence_test objects.
173 """
174 for machine in machines:
175 timeout_mins = 0
176 for test in server_tests:
177 timeout_mins += test.schedule(job, timeout_mins, machine)