blob: b59205d894a1450d714a889af481b5afb8477091 [file] [log] [blame]
Derek Beckett63e1c442020-08-11 14:49:47 -07001# Lint as: python2, python3
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -07002# Copyright 2015 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Sequence extensions to server_job.
7Adds ability to schedule jobs on given machines.
8"""
9
Derek Beckett63e1c442020-08-11 14:49:47 -070010from __future__ import absolute_import
11from __future__ import division
12from __future__ import print_function
13
Simran Basied9ee482015-09-29 15:17:26 -070014import logging
Simran Basia5522a32015-10-06 11:01:24 -070015import os
Simran Basied9ee482015-09-29 15:17:26 -070016
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070017import common
18from autotest_lib.client.common_lib import control_data
Allen Li352b86a2016-12-14 12:11:27 -080019from autotest_lib.client.common_lib import priorities
Simran Basied9ee482015-09-29 15:17:26 -070020from autotest_lib.server import utils
Simran Basia5522a32015-10-06 11:01:24 -070021from autotest_lib.server.cros.dynamic_suite import control_file_getter
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070022from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
23from autotest_lib.site_utils import job_directories
Derek Beckett63e1c442020-08-11 14:49:47 -070024import six
25from six.moves import range
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070026
27MINUTE_IN_SECS = 60
28HOUR_IN_MINUTES = 60
29HOUR_IN_SECS = HOUR_IN_MINUTES * MINUTE_IN_SECS
30DAY_IN_HOURS = 24
31DAY_IN_SECS = DAY_IN_HOURS*HOUR_IN_SECS
32
33DEFAULT_JOB_TIMEOUT_IN_MINS = 4 * HOUR_IN_MINUTES
34
35class SequenceJob(object):
36 """Define part of a sequence that will be scheduled by the sequence test."""
37
38 CONTROL_FILE = """
39def run(machine):
Simran Basied9ee482015-09-29 15:17:26 -070040 job.run_test('%s', host=hosts.create_host(machine), client_ip=machine%s)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070041
42parallel_simple(run, machines)
43"""
44
Simran Basia5522a32015-10-06 11:01:24 -070045 def __init__(self, name, args=None, iteration=1, duration=None,
46 fetch_control_file=False):
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070047 """
48 Constructor
49
Simran Basia5522a32015-10-06 11:01:24 -070050 @param name: name of the server test to run.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070051 @param args: arguments needed by the server test.
52 @param iteration: number of copy of this test to sechudle
53 @param duration: expected duration of the test (in seconds).
Simran Basia5522a32015-10-06 11:01:24 -070054 @param fetch_control_file: If True, fetch the control file contents
55 from disk. Otherwise uses the template
56 control file.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070057 """
58 self._name = name
59 self._args = args or {}
60 self._iteration = iteration
61 self._duration = duration
Simran Basia5522a32015-10-06 11:01:24 -070062 self._fetch_control_file = fetch_control_file
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070063
64
65 def child_job_name(self, machine, iteration_number):
66 """
67 Return a name for a child job.
68
69 @param machine: machine name on which the test will run.
70 @param iteration_number: number with 0 and self._iteration - 1.
71
72 @returns a unique name based on the machine, the name and the iteration.
73 """
74 name_parts = [machine, self._name]
75 tag = self._args.get('tag')
76 if tag:
77 name_parts.append(tag)
78 if self._iteration > 1:
79 name_parts.append(str(iteration_number))
80 return '_'.join(name_parts)
81
82
83 def child_job_timeout(self):
84 """
85 Get the child job timeout in minutes.
86
87 @param args: arguments sent to the test.
88
89 @returns a timeout value for the test, 4h by default.
90 """
91 if self._duration:
Derek Beckett63e1c442020-08-11 14:49:47 -070092 return 2 * int(self._duration) // MINUTE_IN_SECS
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -070093 # default value:
94 return DEFAULT_JOB_TIMEOUT_IN_MINS
95
96
97 def child_control_file(self):
98 """
Simran Basia5522a32015-10-06 11:01:24 -070099 Generate the child job's control file.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700100
Simran Basia5522a32015-10-06 11:01:24 -0700101 If not fetching the contents, use the template control file and
102 populate the template control file with the test name and expand the
103 arguments list.
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700104
105 @param test: name of the test to run
106 @param args: dictionary of argument for this test.
107
108 @returns a fully built control file to be use for the child job.
109 """
Simran Basia5522a32015-10-06 11:01:24 -0700110 if self._fetch_control_file:
111 # TODO (sbasi): Add arg support.
112 cntl_file_getter = control_file_getter.FileSystemGetter(
113 [os.path.join(os.path.dirname(os.path.realpath(__file__)),
114 '..')])
115 return cntl_file_getter.get_control_file_contents_by_name(
116 self._name)
Simran Basied9ee482015-09-29 15:17:26 -0700117 child_args = ['',]
Derek Beckett63e1c442020-08-11 14:49:47 -0700118 for arg, value in six.iteritems(self._args):
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700119 child_args.append('%s=%s' % (arg, repr(value)))
120 if self._duration:
121 child_args.append('duration=%d' % self._duration)
122 return self.CONTROL_FILE % (self._name, ', '.join(child_args))
123
124
125 def schedule(self, job, timeout_mins, machine):
126 """
127 Sequence a job on the running AFE.
128
129 Will schedule a given test on the job machine(s).
130 Support a subset of tests:
131 - server job
132 - no hostless.
133 - no cleanup around tests.
134
135 @param job: server_job object that will server as parent.
136 @param timeout_mins: timeout to set up: if the test last more than
137 timeout_mins, the test will fail.
138 @param machine: machine to run the test on.
139
140 @returns a maximal time in minutes that the sequence can take.
141 """
142 afe = frontend_wrappers.RetryingAFE(timeout_min=30, delay_sec=10,
143 user=job.user, debug=False)
Prathmesh Prabhu5ad4d652018-05-11 17:33:17 -0700144 # job_directores.get_job_id_or_task_id() will return a non-int opaque id
Jesse McGuire92aaa492022-05-16 14:44:12 -0700145 # for ChromeOS Skylab tasks. But sequences will break in that case
Prathmesh Prabhu5ad4d652018-05-11 17:33:17 -0700146 # anyway, because they try to create AFE jobs internally.
147 current_job_id = int(
148 job_directories.get_job_id_or_task_id(job.resultdir))
Simran Basied9ee482015-09-29 15:17:26 -0700149 logging.debug('Current job id: %s', current_job_id)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700150 runtime_mins = self.child_job_timeout()
Simran Basi527c55f2015-12-14 11:57:17 -0800151 hostname = utils.get_hostname_from_machine(machine)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700152
Derek Beckett63e1c442020-08-11 14:49:47 -0700153 for i in range(0, self._iteration):
Simran Basi527c55f2015-12-14 11:57:17 -0800154 child_job_name = self.child_job_name(hostname, i)
Simran Basied9ee482015-09-29 15:17:26 -0700155 logging.debug('Creating job: %s', child_job_name)
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700156 afe.create_job(
157 self.child_control_file(),
Simran Basied9ee482015-09-29 15:17:26 -0700158 name=child_job_name,
Allen Li352b86a2016-12-14 12:11:27 -0800159 priority=priorities.Priority.DEFAULT,
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700160 control_type=control_data.CONTROL_TYPE.SERVER,
Simran Basi527c55f2015-12-14 11:57:17 -0800161 hosts=[hostname], meta_hosts=(), one_time_hosts=(),
Allen Li20a5d8b2017-02-01 17:10:59 -0800162 synch_count=None, is_template=False,
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700163 timeout_mins=timeout_mins + (i + 1) * runtime_mins,
164 max_runtime_mins=runtime_mins,
165 run_verify=False, email_list='', dependencies=(),
166 reboot_before=None, reboot_after=None,
167 parse_failed_repair=None,
168 hostless=False, keyvals=None,
169 drone_set=None, image=None,
170 parent_job_id=current_job_id, test_retry=0, run_reset=False,
Simran Basied9ee482015-09-29 15:17:26 -0700171 require_ssp=utils.is_in_container())
Gwendal Grignou4cf71ad2015-05-13 14:21:29 -0700172 return runtime_mins * self._iteration
173
174
175def sequence_schedule(job, machines, server_tests):
176 """
177 Schedule the tests to run
178
179 Launch all the tests in the sequence on all machines.
180 Returns as soon as the jobs are launched.
181
182 @param job: Job running.
183 @param machines: machine to run on.
184 @param server_tests: Array of sequence_test objects.
185 """
186 for machine in machines:
187 timeout_mins = 0
188 for test in server_tests:
189 timeout_mins += test.schedule(job, timeout_mins, machine)