xref: /aosp_15_r20/external/autotest/server/sequence.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Li"""Sequence extensions to server_job.
7*9c5db199SXin LiAdds ability to schedule jobs on given machines.
8*9c5db199SXin Li"""
9*9c5db199SXin Li
10*9c5db199SXin Lifrom __future__ import absolute_import
11*9c5db199SXin Lifrom __future__ import division
12*9c5db199SXin Lifrom __future__ import print_function
13*9c5db199SXin Li
14*9c5db199SXin Liimport logging
15*9c5db199SXin Liimport os
16*9c5db199SXin Li
17*9c5db199SXin Liimport common
18*9c5db199SXin Lifrom autotest_lib.client.common_lib import control_data
19*9c5db199SXin Lifrom autotest_lib.client.common_lib import priorities
20*9c5db199SXin Lifrom autotest_lib.server import utils
21*9c5db199SXin Lifrom autotest_lib.server.cros.dynamic_suite import control_file_getter
22*9c5db199SXin Lifrom autotest_lib.server.cros.dynamic_suite import frontend_wrappers
23*9c5db199SXin Lifrom autotest_lib.site_utils import job_directories
24*9c5db199SXin Liimport six
25*9c5db199SXin Lifrom six.moves import range
26*9c5db199SXin Li
27*9c5db199SXin LiMINUTE_IN_SECS = 60
28*9c5db199SXin LiHOUR_IN_MINUTES = 60
29*9c5db199SXin LiHOUR_IN_SECS = HOUR_IN_MINUTES * MINUTE_IN_SECS
30*9c5db199SXin LiDAY_IN_HOURS = 24
31*9c5db199SXin LiDAY_IN_SECS = DAY_IN_HOURS*HOUR_IN_SECS
32*9c5db199SXin Li
33*9c5db199SXin LiDEFAULT_JOB_TIMEOUT_IN_MINS = 4 * HOUR_IN_MINUTES
34*9c5db199SXin Li
35*9c5db199SXin Liclass SequenceJob(object):
36*9c5db199SXin Li    """Define part of a sequence that will be scheduled by the sequence test."""
37*9c5db199SXin Li
38*9c5db199SXin Li    CONTROL_FILE = """
39*9c5db199SXin Lidef run(machine):
40*9c5db199SXin Li    job.run_test('%s', host=hosts.create_host(machine), client_ip=machine%s)
41*9c5db199SXin Li
42*9c5db199SXin Liparallel_simple(run, machines)
43*9c5db199SXin Li"""
44*9c5db199SXin Li
45*9c5db199SXin Li    def __init__(self, name, args=None, iteration=1, duration=None,
46*9c5db199SXin Li                 fetch_control_file=False):
47*9c5db199SXin Li        """
48*9c5db199SXin Li        Constructor
49*9c5db199SXin Li
50*9c5db199SXin Li        @param name: name of the server test to run.
51*9c5db199SXin Li        @param args: arguments needed by the server test.
52*9c5db199SXin Li        @param iteration: number of copy of this test to sechudle
53*9c5db199SXin Li        @param duration: expected duration of the test (in seconds).
54*9c5db199SXin Li        @param fetch_control_file: If True, fetch the control file contents
55*9c5db199SXin Li                                   from disk. Otherwise uses the template
56*9c5db199SXin Li                                   control file.
57*9c5db199SXin Li        """
58*9c5db199SXin Li        self._name = name
59*9c5db199SXin Li        self._args = args or {}
60*9c5db199SXin Li        self._iteration = iteration
61*9c5db199SXin Li        self._duration = duration
62*9c5db199SXin Li        self._fetch_control_file = fetch_control_file
63*9c5db199SXin Li
64*9c5db199SXin Li
65*9c5db199SXin Li    def child_job_name(self, machine, iteration_number):
66*9c5db199SXin Li        """
67*9c5db199SXin Li        Return a name for a child job.
68*9c5db199SXin Li
69*9c5db199SXin Li        @param machine: machine name on which the test will run.
70*9c5db199SXin Li        @param iteration_number: number with 0 and self._iteration - 1.
71*9c5db199SXin Li
72*9c5db199SXin Li        @returns a unique name based on the machine, the name and the iteration.
73*9c5db199SXin Li        """
74*9c5db199SXin Li        name_parts = [machine, self._name]
75*9c5db199SXin Li        tag = self._args.get('tag')
76*9c5db199SXin Li        if tag:
77*9c5db199SXin Li            name_parts.append(tag)
78*9c5db199SXin Li        if self._iteration > 1:
79*9c5db199SXin Li            name_parts.append(str(iteration_number))
80*9c5db199SXin Li        return '_'.join(name_parts)
81*9c5db199SXin Li
82*9c5db199SXin Li
83*9c5db199SXin Li    def child_job_timeout(self):
84*9c5db199SXin Li        """
85*9c5db199SXin Li        Get the child job timeout in minutes.
86*9c5db199SXin Li
87*9c5db199SXin Li        @param args: arguments sent to the test.
88*9c5db199SXin Li
89*9c5db199SXin Li        @returns a timeout value for the test, 4h by default.
90*9c5db199SXin Li        """
91*9c5db199SXin Li        if self._duration:
92*9c5db199SXin Li            return 2 * int(self._duration) // MINUTE_IN_SECS
93*9c5db199SXin Li        # default value:
94*9c5db199SXin Li        return DEFAULT_JOB_TIMEOUT_IN_MINS
95*9c5db199SXin Li
96*9c5db199SXin Li
97*9c5db199SXin Li    def child_control_file(self):
98*9c5db199SXin Li        """
99*9c5db199SXin Li        Generate the child job's control file.
100*9c5db199SXin Li
101*9c5db199SXin Li        If not fetching the contents, use the template control file and
102*9c5db199SXin Li        populate the template control file with the test name and expand the
103*9c5db199SXin Li        arguments list.
104*9c5db199SXin Li
105*9c5db199SXin Li        @param test: name of the test to run
106*9c5db199SXin Li        @param args: dictionary of argument for this test.
107*9c5db199SXin Li
108*9c5db199SXin Li        @returns a fully built control file to be use for the child job.
109*9c5db199SXin Li        """
110*9c5db199SXin Li        if self._fetch_control_file:
111*9c5db199SXin Li            # TODO (sbasi): Add arg support.
112*9c5db199SXin Li            cntl_file_getter = control_file_getter.FileSystemGetter(
113*9c5db199SXin Li                    [os.path.join(os.path.dirname(os.path.realpath(__file__)),
114*9c5db199SXin Li                                  '..')])
115*9c5db199SXin Li            return cntl_file_getter.get_control_file_contents_by_name(
116*9c5db199SXin Li                    self._name)
117*9c5db199SXin Li        child_args = ['',]
118*9c5db199SXin Li        for arg, value in six.iteritems(self._args):
119*9c5db199SXin Li            child_args.append('%s=%s' % (arg, repr(value)))
120*9c5db199SXin Li        if self._duration:
121*9c5db199SXin Li            child_args.append('duration=%d' % self._duration)
122*9c5db199SXin Li        return self.CONTROL_FILE % (self._name, ', '.join(child_args))
123*9c5db199SXin Li
124*9c5db199SXin Li
125*9c5db199SXin Li    def schedule(self, job, timeout_mins, machine):
126*9c5db199SXin Li        """
127*9c5db199SXin Li        Sequence a job on the running AFE.
128*9c5db199SXin Li
129*9c5db199SXin Li        Will schedule a given test on the job machine(s).
130*9c5db199SXin Li        Support a subset of tests:
131*9c5db199SXin Li        - server job
132*9c5db199SXin Li        - no hostless.
133*9c5db199SXin Li        - no cleanup around tests.
134*9c5db199SXin Li
135*9c5db199SXin Li        @param job: server_job object that will server as parent.
136*9c5db199SXin Li        @param timeout_mins: timeout to set up: if the test last more than
137*9c5db199SXin Li           timeout_mins, the test will fail.
138*9c5db199SXin Li        @param machine: machine to run the test on.
139*9c5db199SXin Li
140*9c5db199SXin Li        @returns a maximal time in minutes that the sequence can take.
141*9c5db199SXin Li        """
142*9c5db199SXin Li        afe = frontend_wrappers.RetryingAFE(timeout_min=30, delay_sec=10,
143*9c5db199SXin Li                                            user=job.user, debug=False)
144*9c5db199SXin Li        # job_directores.get_job_id_or_task_id() will return a non-int opaque id
145*9c5db199SXin Li        # for ChromeOS Skylab tasks. But sequences will break in that case
146*9c5db199SXin Li        # anyway, because they try to create AFE jobs internally.
147*9c5db199SXin Li        current_job_id = int(
148*9c5db199SXin Li                job_directories.get_job_id_or_task_id(job.resultdir))
149*9c5db199SXin Li        logging.debug('Current job id: %s', current_job_id)
150*9c5db199SXin Li        runtime_mins = self.child_job_timeout()
151*9c5db199SXin Li        hostname = utils.get_hostname_from_machine(machine)
152*9c5db199SXin Li
153*9c5db199SXin Li        for i in range(0, self._iteration):
154*9c5db199SXin Li            child_job_name = self.child_job_name(hostname, i)
155*9c5db199SXin Li            logging.debug('Creating job: %s', child_job_name)
156*9c5db199SXin Li            afe.create_job(
157*9c5db199SXin Li                    self.child_control_file(),
158*9c5db199SXin Li                    name=child_job_name,
159*9c5db199SXin Li                    priority=priorities.Priority.DEFAULT,
160*9c5db199SXin Li                    control_type=control_data.CONTROL_TYPE.SERVER,
161*9c5db199SXin Li                    hosts=[hostname], meta_hosts=(), one_time_hosts=(),
162*9c5db199SXin Li                    synch_count=None, is_template=False,
163*9c5db199SXin Li                    timeout_mins=timeout_mins + (i + 1) * runtime_mins,
164*9c5db199SXin Li                    max_runtime_mins=runtime_mins,
165*9c5db199SXin Li                    run_verify=False, email_list='', dependencies=(),
166*9c5db199SXin Li                    reboot_before=None, reboot_after=None,
167*9c5db199SXin Li                    parse_failed_repair=None,
168*9c5db199SXin Li                    hostless=False, keyvals=None,
169*9c5db199SXin Li                    drone_set=None, image=None,
170*9c5db199SXin Li                    parent_job_id=current_job_id, test_retry=0, run_reset=False,
171*9c5db199SXin Li                    require_ssp=utils.is_in_container())
172*9c5db199SXin Li        return runtime_mins * self._iteration
173*9c5db199SXin Li
174*9c5db199SXin Li
175*9c5db199SXin Lidef sequence_schedule(job, machines, server_tests):
176*9c5db199SXin Li    """
177*9c5db199SXin Li    Schedule the tests to run
178*9c5db199SXin Li
179*9c5db199SXin Li    Launch all the tests in the sequence on all machines.
180*9c5db199SXin Li    Returns as soon as the jobs are launched.
181*9c5db199SXin Li
182*9c5db199SXin Li    @param job: Job running.
183*9c5db199SXin Li    @param machines: machine to run on.
184*9c5db199SXin Li    @param server_tests: Array of sequence_test objects.
185*9c5db199SXin Li    """
186*9c5db199SXin Li    for machine in machines:
187*9c5db199SXin Li        timeout_mins = 0
188*9c5db199SXin Li        for test in server_tests:
189*9c5db199SXin Li            timeout_mins += test.schedule(job, timeout_mins, machine)
190