blob: 365f683252c829dee5323054b8d0d408fcf266af [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
10
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070011import common
12from autotest_lib.client.common_lib import control_data
Simran Basied9ee482015-09-29 15:17:26 -070013from autotest_lib.server import utils
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070014from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
15from autotest_lib.site_utils import job_directories
16
17MINUTE_IN_SECS = 60
18HOUR_IN_MINUTES = 60
19HOUR_IN_SECS = HOUR_IN_MINUTES * MINUTE_IN_SECS
20DAY_IN_HOURS = 24
21DAY_IN_SECS = DAY_IN_HOURS*HOUR_IN_SECS
22
23DEFAULT_JOB_TIMEOUT_IN_MINS = 4 * HOUR_IN_MINUTES
24
25class SequenceJob(object):
26 """Define part of a sequence that will be scheduled by the sequence test."""
27
28 CONTROL_FILE = """
29def run(machine):
Simran Basied9ee482015-09-29 15:17:26 -070030 job.run_test('%s', host=hosts.create_host(machine), client_ip=machine%s)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070031
32parallel_simple(run, machines)
33"""
34
Simran Basied9ee482015-09-29 15:17:26 -070035 def __init__(self, name, args=None, iteration=1, duration=None):
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070036 """
37 Constructor
38
39 @param name: name of the sever test to run.
40 @param args: arguments needed by the server test.
41 @param iteration: number of copy of this test to sechudle
42 @param duration: expected duration of the test (in seconds).
43 """
44 self._name = name
45 self._args = args or {}
46 self._iteration = iteration
47 self._duration = duration
48
49
50 def child_job_name(self, machine, iteration_number):
51 """
52 Return a name for a child job.
53
54 @param machine: machine name on which the test will run.
55 @param iteration_number: number with 0 and self._iteration - 1.
56
57 @returns a unique name based on the machine, the name and the iteration.
58 """
59 name_parts = [machine, self._name]
60 tag = self._args.get('tag')
61 if tag:
62 name_parts.append(tag)
63 if self._iteration > 1:
64 name_parts.append(str(iteration_number))
65 return '_'.join(name_parts)
66
67
68 def child_job_timeout(self):
69 """
70 Get the child job timeout in minutes.
71
72 @param args: arguments sent to the test.
73
74 @returns a timeout value for the test, 4h by default.
75 """
76 if self._duration:
77 return 2 * int(self._duration) / MINUTE_IN_SECS
78 # default value:
79 return DEFAULT_JOB_TIMEOUT_IN_MINS
80
81
82 def child_control_file(self):
83 """
84 Populate the template control file.
85
86 Populate it with the test name and expand the arguments
87 list.
88
89 @param test: name of the test to run
90 @param args: dictionary of argument for this test.
91
92 @returns a fully built control file to be use for the child job.
93 """
Simran Basied9ee482015-09-29 15:17:26 -070094 child_args = ['',]
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070095 for arg, value in self._args.iteritems():
96 child_args.append('%s=%s' % (arg, repr(value)))
97 if self._duration:
98 child_args.append('duration=%d' % self._duration)
99 return self.CONTROL_FILE % (self._name, ', '.join(child_args))
100
101
102 def schedule(self, job, timeout_mins, machine):
103 """
104 Sequence a job on the running AFE.
105
106 Will schedule a given test on the job machine(s).
107 Support a subset of tests:
108 - server job
109 - no hostless.
110 - no cleanup around tests.
111
112 @param job: server_job object that will server as parent.
113 @param timeout_mins: timeout to set up: if the test last more than
114 timeout_mins, the test will fail.
115 @param machine: machine to run the test on.
116
117 @returns a maximal time in minutes that the sequence can take.
118 """
119 afe = frontend_wrappers.RetryingAFE(timeout_min=30, delay_sec=10,
120 user=job.user, debug=False)
121 current_job_id = job_directories.get_job_id_or_task_id(job.resultdir)
Simran Basied9ee482015-09-29 15:17:26 -0700122 logging.debug('Current job id: %s', current_job_id)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700123 runtime_mins = self.child_job_timeout()
124
125 for i in xrange(0, self._iteration):
Simran Basied9ee482015-09-29 15:17:26 -0700126 child_job_name = self.child_job_name(machine, i)
127 logging.debug('Creating job: %s', child_job_name)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700128 afe.create_job(
129 self.child_control_file(),
Simran Basied9ee482015-09-29 15:17:26 -0700130 name=child_job_name,
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700131 priority='Medium',
132 control_type=control_data.CONTROL_TYPE.SERVER,
133 hosts=[machine], meta_hosts=(), one_time_hosts=(),
134 atomic_group_name=None, synch_count=None, is_template=False,
135 timeout_mins=timeout_mins + (i + 1) * runtime_mins,
136 max_runtime_mins=runtime_mins,
137 run_verify=False, email_list='', dependencies=(),
138 reboot_before=None, reboot_after=None,
139 parse_failed_repair=None,
140 hostless=False, keyvals=None,
141 drone_set=None, image=None,
142 parent_job_id=current_job_id, test_retry=0, run_reset=False,
Simran Basied9ee482015-09-29 15:17:26 -0700143 require_ssp=utils.is_in_container())
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700144 return runtime_mins * self._iteration
145
146
147def sequence_schedule(job, machines, server_tests):
148 """
149 Schedule the tests to run
150
151 Launch all the tests in the sequence on all machines.
152 Returns as soon as the jobs are launched.
153
154 @param job: Job running.
155 @param machines: machine to run on.
156 @param server_tests: Array of sequence_test objects.
157 """
158 for machine in machines:
159 timeout_mins = 0
160 for test in server_tests:
161 timeout_mins += test.schedule(job, timeout_mins, machine)