blob: 856cd218beaa6b619c8fdb6ced42fe911b7124df [file] [log] [blame]
Xixuan Wuc7bf77c2018-04-24 12:05:40 -07001# Copyright 2018 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"""Definition of a CrOS suite in skylab.
6
7This file is a simplicication of dynamic_suite.suite without any useless
8features for skylab suite.
9
10Suite class in this file mainly has 2 features:
11 1. Integrate parameters from control file & passed in arguments.
12 2. Find proper child tests for a given suite.
13
14Use case:
15 See _run_suite() in skylab_suite.run_suite_skylab.
16"""
17
18from __future__ import absolute_import
19from __future__ import division
20from __future__ import print_function
21
Xixuan Wu7cc10e52018-04-25 17:04:51 -070022import collections
Xixuan Wu2406be32018-05-14 13:51:30 -070023import logging
Xixuan Wu8873a0e2018-07-20 17:51:33 -070024import os
Xixuan Wu7cc10e52018-04-25 17:04:51 -070025
Xixuan Wu48c45b92018-04-26 11:09:35 -070026from lucifer import autotest
Xixuan Wu89803382018-07-27 12:33:33 -070027from skylab_suite import errors
Xixuan Wu2406be32018-05-14 13:51:30 -070028from skylab_suite import swarming_lib
Xixuan Wu48c45b92018-04-26 11:09:35 -070029
Xixuan Wu7cc10e52018-04-25 17:04:51 -070030
Xixuan Wu9287fda2018-07-12 16:22:06 -070031SuiteSpec = collections.namedtuple(
32 'SuiteSpec',
Xixuan Wu7cc10e52018-04-25 17:04:51 -070033 [
34 'builds',
Xixuan Wu6c041332018-05-07 16:04:36 -070035 'suite_name',
Xixuan Wue3e362f2018-04-26 16:34:28 -070036 'suite_file_name',
Xixuan Wu7cc10e52018-04-25 17:04:51 -070037 'test_source_build',
Xixuan Wu2406be32018-05-14 13:51:30 -070038 'suite_args',
Xixuan Wu70217a92018-06-04 16:43:42 -070039 'priority',
Xixuan Wu77d4a592018-06-08 10:40:57 -070040 'board',
Xixuan Wu3dea7cf2018-12-10 17:50:45 -080041 'model',
Xixuan Wu77d4a592018-06-08 10:40:57 -070042 'pool',
Xixuan Wub2795662018-06-28 16:02:53 -070043 'job_keyvals',
Xixuan Wu0956b632018-08-06 14:40:23 -070044 'minimum_duts',
Xixuan Wuba0276c2018-08-10 09:36:23 -070045 'timeout_mins',
Aviv Keshetbe570b32018-12-06 14:46:19 -080046 'quota_account',
Xixuan Wu56424bc2018-05-15 11:03:27 -070047 ])
48
Xixuan Wu834cb4b2018-07-12 16:33:49 -070049SuiteHandlerSpec = collections.namedtuple(
50 'SuiteHandlerSpec',
Xixuan Wu56424bc2018-05-15 11:03:27 -070051 [
Xixuan Wu08354a02018-08-01 09:15:26 -070052 'suite_name',
Xixuan Wuf2da1952018-07-10 10:19:42 -070053 'wait',
Xixuan Wuc7430712018-07-10 12:04:34 -070054 'suite_id',
Xixuan Wu2406be32018-05-14 13:51:30 -070055 'timeout_mins',
Xixuan Wuc7dce5ef2018-08-19 21:09:53 -070056 'passed_mins',
Xixuan Wu56424bc2018-05-15 11:03:27 -070057 'test_retry',
58 'max_retries',
59 'provision_num_required',
Xixuan Wu7cc10e52018-04-25 17:04:51 -070060 ])
61
Xixuan Wu9d5d7032018-07-12 16:44:02 -070062TestHandlerSpec = collections.namedtuple(
63 'TestHandlerSpec',
Xixuan Wu5cb5a402018-06-04 16:37:23 -070064 [
Xixuan Wu5811e832018-07-12 11:56:24 -070065 'test_spec',
Xixuan Wu5cb5a402018-06-04 16:37:23 -070066 'remaining_retries',
67 'previous_retried_ids',
68 ])
69
Xixuan Wu9d5d7032018-07-12 16:44:02 -070070TestSpec = collections.namedtuple(
Xixuan Wu5811e832018-07-12 11:56:24 -070071 'TestSpec',
Xixuan Wu9af95a22018-05-18 10:46:42 -070072 [
73 'test',
Xixuan Wu70217a92018-06-04 16:43:42 -070074 'priority',
Xixuan Wu77d4a592018-06-08 10:40:57 -070075 'board',
Xixuan Wu3dea7cf2018-12-10 17:50:45 -080076 'model',
Xixuan Wu77d4a592018-06-08 10:40:57 -070077 'pool',
Xixuan Wu5cb5a402018-06-04 16:37:23 -070078 'build',
Xixuan Wub2795662018-06-28 16:02:53 -070079 'keyvals',
Aviv Keshetf0951212019-03-18 14:54:32 -070080 # TODO(akeshet): Determine why this is necessary
81 # (can't this just be specified as its own dimension?) and
82 # delete it if it isn't necessary.
Xixuan Wu0c01b092018-06-13 14:12:55 -070083 'bot_id',
Xixuan Wuff19abe2018-06-20 10:44:45 -070084 'dut_name',
Xixuan Wu5cb5a402018-06-04 16:37:23 -070085 'expiration_secs',
86 'grace_period_secs',
Aviv Keshetf0f1b492019-04-03 15:30:48 -070087 'execution_timeout_mins',
Xixuan Wu5cb5a402018-06-04 16:37:23 -070088 'io_timeout_secs',
Aviv Keshetbe570b32018-12-06 14:46:19 -080089 'quota_account',
Xixuan Wu9af95a22018-05-18 10:46:42 -070090 ])
91
Xixuan Wu7cc10e52018-04-25 17:04:51 -070092
Xixuan Wu9af95a22018-05-18 10:46:42 -070093class SuiteHandler(object):
94 """The class for handling a CrOS suite run.
95
96 Its responsibility includes handling retries for child tests.
97 """
Xixuan Wu2406be32018-05-14 13:51:30 -070098
Aviv Keshetd3adbfa2019-03-19 11:43:24 -070099 def __init__(self, specs, client):
Xixuan Wu08354a02018-08-01 09:15:26 -0700100 self._suite_name = specs.suite_name
Xixuan Wuf2da1952018-07-10 10:19:42 -0700101 self._wait = specs.wait
Xixuan Wu9af95a22018-05-18 10:46:42 -0700102 self._timeout_mins = specs.timeout_mins
103 self._provision_num_required = specs.provision_num_required
104 self._test_retry = specs.test_retry
105 self._max_retries = specs.max_retries
Xixuan Wuc7dce5ef2018-08-19 21:09:53 -0700106 self.passed_mins = specs.passed_mins
Xixuan Wu56424bc2018-05-15 11:03:27 -0700107
Xixuan Wu8873a0e2018-07-20 17:51:33 -0700108 # The swarming task id of the suite that this suite_handler is handling.
Xixuan Wuc7430712018-07-10 12:04:34 -0700109 self._suite_id = specs.suite_id
Xixuan Wu8873a0e2018-07-20 17:51:33 -0700110 # The swarming task id of current run_suite_skylab process. It could be
111 # different from self._suite_id if a suite_id is passed in.
112 self._task_id = os.environ.get('SWARMING_TASK_ID')
Xixuan Wu9af95a22018-05-18 10:46:42 -0700113 self._task_to_test_maps = {}
Xixuan Wu415e8212018-06-04 17:01:12 -0700114 self.successfully_provisioned_duts = set()
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700115 self._client = client
Xixuan Wu9af95a22018-05-18 10:46:42 -0700116
117 # It only maintains the swarming task of the final run of each
118 # child task, i.e. it doesn't include failed swarming tasks of
119 # each child task which will get retried later.
Xixuan Wu2406be32018-05-14 13:51:30 -0700120 self._active_child_tasks = []
121
Xixuan Wu9af95a22018-05-18 10:46:42 -0700122 def should_wait(self):
123 """Return whether to wait for a suite's result."""
124 return self._wait
125
Xixuan Wu415e8212018-06-04 17:01:12 -0700126 def is_provision(self):
127 """Return whether the suite handler is for provision suite."""
Xixuan Wu08354a02018-08-01 09:15:26 -0700128 return self._suite_name == 'provision'
Xixuan Wu415e8212018-06-04 17:01:12 -0700129
Xixuan Wu9af95a22018-05-18 10:46:42 -0700130 def set_suite_id(self, suite_id):
131 """Set swarming task id for a suite.
132
133 @param suite_id: The swarming task id of this suite.
134 """
135 self._suite_id = suite_id
136
Xixuan Wu9d5d7032018-07-12 16:44:02 -0700137 def add_test_by_task_id(self, task_id, test_handler_spec):
Xixuan Wu9af95a22018-05-18 10:46:42 -0700138 """Record a child test and its swarming task id.
139
140 @param task_id: the swarming task id of a child test.
Xixuan Wu9d5d7032018-07-12 16:44:02 -0700141 @param test_handler_spec: a TestHandlerSpec object.
Xixuan Wu9af95a22018-05-18 10:46:42 -0700142 """
Xixuan Wu9d5d7032018-07-12 16:44:02 -0700143 self._task_to_test_maps[task_id] = test_handler_spec
Xixuan Wu9af95a22018-05-18 10:46:42 -0700144
145 def get_test_by_task_id(self, task_id):
146 """Get a child test by its swarming task id.
147
148 @param task_id: the swarming task id of a child test.
149 """
150 return self._task_to_test_maps[task_id]
151
152 def remove_test_by_task_id(self, task_id):
153 """Delete a child test by its swarming task id.
154
155 @param task_id: the swarming task id of a child test.
156 """
157 self._task_to_test_maps.pop(task_id, None)
158
159 def set_max_retries(self, max_retries):
160 """Set the max retries for a suite.
161
162 @param max_retries: The current maximum retries to set.
163 """
164 self._max_retries = max_retries
165
166 @property
Xixuan Wu61d62832018-07-20 17:10:19 -0700167 def task_to_test_maps(self):
168 """Get the task_to_test_maps of a suite."""
169 return self._task_to_test_maps
170
171 @property
Xixuan Wu9af95a22018-05-18 10:46:42 -0700172 def timeout_mins(self):
173 """Get the timeout minutes of a suite."""
174 return self._timeout_mins
175
176 @property
177 def suite_id(self):
178 """Get the swarming task id of a suite."""
179 return self._suite_id
180
181 @property
Xixuan Wu8873a0e2018-07-20 17:51:33 -0700182 def task_id(self):
183 """Get swarming task id of current process."""
184 return self._task_id
185
186 @property
Xixuan Wu9af95a22018-05-18 10:46:42 -0700187 def max_retries(self):
188 """Get the max num of retries of a suite."""
189 return self._max_retries
190
Xixuan Wuc6e28d32018-08-27 14:48:14 -0700191 def get_active_child_tasks(self, suite_id):
Xixuan Wu9af95a22018-05-18 10:46:42 -0700192 """Get the child tasks which is actively monitored by a suite.
193
194 The active child tasks list includes tasks which are currently running
195 or finished without following retries. E.g.
196 Suite task X:
197 child task 1: x1 (first try x1_1, second try x1_2)
198 child task 2: x2 (first try: x2_1)
199 The final active child task list will include task x1_2 and x2_1, won't
200 include x1_1 since it's a task which is finished but get retried later.
201 """
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700202 all_tasks = self._client.get_child_tasks(suite_id)
Xixuan Wuc6e28d32018-08-27 14:48:14 -0700203 return [t for t in all_tasks if t['task_id'] in self._task_to_test_maps]
Xixuan Wu2406be32018-05-14 13:51:30 -0700204
Xixuan Wuc6e28d32018-08-27 14:48:14 -0700205 def handle_results(self, suite_id):
Xixuan Wu2406be32018-05-14 13:51:30 -0700206 """Handle child tasks' results."""
Xixuan Wuc6e28d32018-08-27 14:48:14 -0700207 self._active_child_tasks = self.get_active_child_tasks(suite_id)
208 self.retried_tasks = [t for t in self._active_child_tasks
209 if self._should_retry(t)]
Xixuan Wu56424bc2018-05-15 11:03:27 -0700210 logging.info('Found %d tests to be retried.', len(self.retried_tasks))
Xixuan Wu2406be32018-05-14 13:51:30 -0700211
Xixuan Wu415e8212018-06-04 17:01:12 -0700212 def _check_all_tasks_finished(self):
213 """Check whether all tasks are finished, including retried tasks."""
Xixuan Wu56424bc2018-05-15 11:03:27 -0700214 finished_tasks = [t for t in self._active_child_tasks if
215 t['state'] in swarming_lib.TASK_FINISHED_STATUS]
216 logging.info('%d/%d child tasks finished, %d got retried.',
217 len(finished_tasks), len(self._active_child_tasks),
218 len(self.retried_tasks))
219 return (len(finished_tasks) == len(self._active_child_tasks)
220 and not self.retried_tasks)
221
Xixuan Wu415e8212018-06-04 17:01:12 -0700222 def _set_successful_provisioned_duts(self):
223 """Set successfully provisioned duts."""
224 for t in self._active_child_tasks:
225 if (swarming_lib.get_task_final_state(t) ==
226 swarming_lib.TASK_COMPLETED_SUCCESS):
Xixuan Wuff19abe2018-06-20 10:44:45 -0700227 dut_name = self.get_test_by_task_id(
Xixuan Wu5811e832018-07-12 11:56:24 -0700228 t['task_id']).test_spec.dut_name
Xixuan Wuff19abe2018-06-20 10:44:45 -0700229 if dut_name:
Xixuan Wu415e8212018-06-04 17:01:12 -0700230 self.successfully_provisioned_duts.add(dut_name)
231
232 def is_provision_successfully_finished(self):
233 """Check whether provision succeeds."""
234 logging.info('Found %d successfully provisioned duts, '
235 'the minimum requirement is %d',
236 len(self.successfully_provisioned_duts),
237 self._provision_num_required)
238 return (len(self.successfully_provisioned_duts) >=
239 self._provision_num_required)
240
241 def is_finished_waiting(self):
242 """Check whether the suite should finish its waiting."""
243 if self.is_provision():
244 self._set_successful_provisioned_duts()
245 return (self.is_provision_successfully_finished() or
246 self._check_all_tasks_finished())
247
248 return self._check_all_tasks_finished()
249
Xixuan Wu56424bc2018-05-15 11:03:27 -0700250 def _should_retry(self, test_result):
251 """Check whether a test should be retried.
252
253 We will retry a test if:
254 1. The test-level retry is enabled for this suite.
255 2. The test fails.
256 3. The test is currently monitored by the suite, i.e.
257 it's not a previous retried test.
258 4. The test has remaining retries based on JOB_RETRIES in
259 its control file.
260 5. The suite-level max retries isn't hit.
261
262 @param test_result: A json test result from swarming API.
263
264 @return True if we should retry the test.
265 """
266 task_id = test_result['task_id']
267 state = test_result['state']
268 is_failure = test_result['failure']
Xixuan Wu9af95a22018-05-18 10:46:42 -0700269 return (self._test_retry and
Xixuan Wu56424bc2018-05-15 11:03:27 -0700270 ((state == swarming_lib.TASK_COMPLETED and is_failure)
Xixuan Wuaff23c72018-06-14 12:10:44 -0700271 or (state in swarming_lib.TASK_STATUS_TO_RETRY))
Xixuan Wu9af95a22018-05-18 10:46:42 -0700272 and (task_id in self._task_to_test_maps)
273 and (self._task_to_test_maps[task_id].remaining_retries > 0)
274 and (self._max_retries > 0))
Xixuan Wu2406be32018-05-14 13:51:30 -0700275
276
Xixuan Wuc7bf77c2018-04-24 12:05:40 -0700277class Suite(object):
278 """The class for a CrOS suite."""
Xixuan Wu606e2182018-06-14 11:30:35 -0700279 EXPIRATION_SECS = swarming_lib.DEFAULT_EXPIRATION_SECS
Xixuan Wuc7bf77c2018-04-24 12:05:40 -0700280
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700281 def __init__(self, spec, client):
Xixuan Wu7cc10e52018-04-25 17:04:51 -0700282 """Initialize a suite.
283
Xixuan Wu9287fda2018-07-12 16:22:06 -0700284 @param spec: A SuiteSpec object.
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700285 @param client: A swarming_lib.Client instance.
Xixuan Wu7cc10e52018-04-25 17:04:51 -0700286 """
Xixuan Wue3e362f2018-04-26 16:34:28 -0700287 self._ds = None
288
289 self.control_file = ''
Xixuan Wu5811e832018-07-12 11:56:24 -0700290 self.test_specs = []
Xixuan Wu9287fda2018-07-12 16:22:06 -0700291 self.builds = spec.builds
292 self.test_source_build = spec.test_source_build
293 self.suite_name = spec.suite_name
294 self.suite_file_name = spec.suite_file_name
295 self.priority = spec.priority
296 self.board = spec.board
Xixuan Wu3dea7cf2018-12-10 17:50:45 -0800297 self.model = spec.model
Xixuan Wu9287fda2018-07-12 16:22:06 -0700298 self.pool = spec.pool
299 self.job_keyvals = spec.job_keyvals
Xixuan Wu0956b632018-08-06 14:40:23 -0700300 self.minimum_duts = spec.minimum_duts
Xixuan Wuba0276c2018-08-10 09:36:23 -0700301 self.timeout_mins = spec.timeout_mins
Aviv Keshetbe570b32018-12-06 14:46:19 -0800302 self.quota_account = spec.quota_account
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700303 self._client = client
Xixuan Wu48c45b92018-04-26 11:09:35 -0700304
Xixuan Wue3e362f2018-04-26 16:34:28 -0700305 @property
306 def ds(self):
307 """Getter for private |self._ds| property.
308
309 This ensures that once self.ds is called, there's a devserver ready
310 for it.
311 """
312 if self._ds is None:
Xixuan Wu89803382018-07-27 12:33:33 -0700313 raise errors.InValidPropertyError(
Xixuan Wue3e362f2018-04-26 16:34:28 -0700314 'Property self.ds is None. Please call stage_suite_artifacts() '
315 'before calling it.')
316
317 return self._ds
318
Xixuan Wub2795662018-06-28 16:02:53 -0700319 def _get_cros_build(self):
320 provision = autotest.load('server.cros.provision')
321 return self.builds.get(provision.CROS_VERSION_PREFIX,
322 self.builds.values()[0])
323
324 def _create_suite_keyvals(self):
325 constants = autotest.load('server.cros.dynamic_suite.constants')
326 provision = autotest.load('server.cros.provision')
327 cros_build = self._get_cros_build()
328 keyvals = {
329 constants.JOB_BUILD_KEY: cros_build,
330 constants.JOB_SUITE_KEY: self.suite_name,
331 constants.JOB_BUILDS_KEY: self.builds
332 }
333 if (cros_build != self.test_source_build or
334 len(self.builds) > 1):
335 keyvals[constants.JOB_TEST_SOURCE_BUILD_KEY] = (
336 self.test_source_build)
337 for prefix, build in self.builds.iteritems():
338 if prefix == provision.FW_RW_VERSION_PREFIX:
339 keyvals[constants.FWRW_BUILD]= build
340 elif prefix == provision.FW_RO_VERSION_PREFIX:
341 keyvals[constants.FWRO_BUILD] = build
342
343 for key in self.job_keyvals:
344 if key in constants.INHERITED_KEYVALS:
345 keyvals[key] = self.job_keyvals[key]
346
347 return keyvals
348
Xixuan Wue3e362f2018-04-26 16:34:28 -0700349 def prepare(self):
350 """Prepare a suite job for execution."""
351 self._stage_suite_artifacts()
352 self._parse_suite_args()
Xixuan Wub2795662018-06-28 16:02:53 -0700353 keyvals = self._create_suite_keyvals()
Xixuan Wu0c01b092018-06-13 14:12:55 -0700354 available_bots = self._get_available_bots()
Xixuan Wu0956b632018-08-06 14:40:23 -0700355 if len(available_bots) < self.minimum_duts:
356 raise errors.NoAvailableDUTsError(
Xixuan Wuefa94522018-08-07 17:51:34 -0700357 self.board, self.pool, len(available_bots),
358 self.minimum_duts)
359
Xixuan Wu0c01b092018-06-13 14:12:55 -0700360 tests = self._find_tests(available_bots_num=len(available_bots))
Xixuan Wu5811e832018-07-12 11:56:24 -0700361 self.test_specs = self._get_test_specs(tests, available_bots, keyvals)
Xixuan Wue3e362f2018-04-26 16:34:28 -0700362
Xixuan Wu19df5f32018-08-07 16:19:44 -0700363 def _create_test_spec(self, test, keyvals, bot_id='', dut_name=''):
364 return TestSpec(
365 test=test,
366 priority=self.priority,
367 board=self.board,
Xixuan Wu3dea7cf2018-12-10 17:50:45 -0800368 model=self.model,
Xixuan Wu19df5f32018-08-07 16:19:44 -0700369 pool=self.pool,
370 build=self.test_source_build,
371 bot_id=bot_id,
372 dut_name=dut_name,
373 keyvals=keyvals,
Xixuan Wuba0276c2018-08-10 09:36:23 -0700374 expiration_secs=self.timeout_mins * 60,
Xixuan Wu19df5f32018-08-07 16:19:44 -0700375 grace_period_secs=swarming_lib.DEFAULT_TIMEOUT_SECS,
Aviv Keshetf0f1b492019-04-03 15:30:48 -0700376 execution_timeout_mins=self.timeout_mins,
Xixuan Wu19df5f32018-08-07 16:19:44 -0700377 io_timeout_secs=swarming_lib.DEFAULT_TIMEOUT_SECS,
Aviv Keshetbe570b32018-12-06 14:46:19 -0800378 quota_account=self.quota_account,
Xixuan Wu19df5f32018-08-07 16:19:44 -0700379 )
Xixuan Wue3e362f2018-04-26 16:34:28 -0700380
Xixuan Wu19df5f32018-08-07 16:19:44 -0700381 def _get_test_specs(self, tests, available_bots, keyvals):
382 return [self._create_test_spec(test, keyvals) for test in tests]
Xixuan Wu0c01b092018-06-13 14:12:55 -0700383
Xixuan Wue3e362f2018-04-26 16:34:28 -0700384 def _stage_suite_artifacts(self):
Xixuan Wu48c45b92018-04-26 11:09:35 -0700385 """Stage suite control files and suite-to-tests mapping file.
386
387 @param build: The build to stage artifacts.
388 """
389 suite_common = autotest.load('server.cros.dynamic_suite.suite_common')
390 ds, _ = suite_common.stage_build_artifacts(self.test_source_build)
Xixuan Wue3e362f2018-04-26 16:34:28 -0700391 self._ds = ds
Xixuan Wu48c45b92018-04-26 11:09:35 -0700392
Xixuan Wue3e362f2018-04-26 16:34:28 -0700393 def _parse_suite_args(self):
Xixuan Wu48c45b92018-04-26 11:09:35 -0700394 """Get the suite args.
395
396 The suite args includes:
397 a. suite args in suite control file.
398 b. passed-in suite args by user.
399 """
Xixuan Wue3e362f2018-04-26 16:34:28 -0700400 suite_common = autotest.load('server.cros.dynamic_suite.suite_common')
401 self.control_file = suite_common.get_control_file_by_build(
402 self.test_source_build, self.ds, self.suite_file_name)
Xixuan Wu6c041332018-05-07 16:04:36 -0700403
Xixuan Wu0c01b092018-06-13 14:12:55 -0700404 def _find_tests(self, available_bots_num=0):
Xixuan Wu6c041332018-05-07 16:04:36 -0700405 """Fetch the child tests."""
406 control_file_getter = autotest.load(
407 'server.cros.dynamic_suite.control_file_getter')
408 suite_common = autotest.load('server.cros.dynamic_suite.suite_common')
Xixuan Wu6c041332018-05-07 16:04:36 -0700409 cf_getter = control_file_getter.DevServerGetter(
410 self.test_source_build, self.ds)
411 tests = suite_common.retrieve_for_suite(
412 cf_getter, self.suite_name)
Xixuan Wu81b71cb2019-01-10 16:00:30 -0800413 return suite_common.filter_tests(
414 tests, suite_common.name_in_tag_predicate(self.suite_name))
Xixuan Wu2406be32018-05-14 13:51:30 -0700415
Xixuan Wu0c01b092018-06-13 14:12:55 -0700416 def _get_available_bots(self):
Xixuan Wu0956b632018-08-06 14:40:23 -0700417 """Get available bots for suites."""
Xixuan Wudb053c82019-01-31 20:07:06 -0800418 dimensions = {'pool': swarming_lib.SKYLAB_DRONE_POOL,
419 'label-board': self.board}
420 swarming_pool_deps = swarming_lib.task_dependencies_from_labels(
421 ['pool:%s' % self.pool])
422 dimensions.update(swarming_pool_deps)
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700423 bots = self._client.query_bots_list(dimensions)
Xixuan Wu0956b632018-08-06 14:40:23 -0700424 return [bot for bot in bots if swarming_lib.bot_available(bot)]
Xixuan Wu0c01b092018-06-13 14:12:55 -0700425
Xixuan Wu2406be32018-05-14 13:51:30 -0700426
427class ProvisionSuite(Suite):
428 """The class for a CrOS provision suite."""
Xixuan Wu6bd67ea2018-08-01 09:24:59 -0700429 EXPIRATION_SECS = swarming_lib.DEFAULT_EXPIRATION_SECS
Xixuan Wu2406be32018-05-14 13:51:30 -0700430
Aviv Keshetd3adbfa2019-03-19 11:43:24 -0700431 def __init__(self, spec, client):
432 super(ProvisionSuite, self).__init__(spec, client)
Xixuan Wu9287fda2018-07-12 16:22:06 -0700433 self._num_required = spec.suite_args['num_required']
Xixuan Wu2406be32018-05-14 13:51:30 -0700434
Xixuan Wu0c01b092018-06-13 14:12:55 -0700435 def _find_tests(self, available_bots_num=0):
Xixuan Wu2406be32018-05-14 13:51:30 -0700436 """Fetch the child tests for provision suite."""
437 control_file_getter = autotest.load(
438 'server.cros.dynamic_suite.control_file_getter')
439 suite_common = autotest.load('server.cros.dynamic_suite.suite_common')
Xixuan Wu2406be32018-05-14 13:51:30 -0700440 cf_getter = control_file_getter.DevServerGetter(
441 self.test_source_build, self.ds)
442 dummy_test = suite_common.retrieve_control_data_for_test(
443 cf_getter, 'dummy_Pass')
Xixuan Wu0c01b092018-06-13 14:12:55 -0700444 logging.info('Get %d available DUTs for provision.', available_bots_num)
Xixuan Wu89803382018-07-27 12:33:33 -0700445 if available_bots_num < self._num_required:
446 logging.warning('Not enough available DUTs for provision.')
447 raise errors.NoAvailableDUTsError(
Xixuan Wuefa94522018-08-07 17:51:34 -0700448 self.board, self.pool, available_bots_num,
449 self._num_required)
Xixuan Wu89803382018-07-27 12:33:33 -0700450
Xixuan Wu0c01b092018-06-13 14:12:55 -0700451 return [dummy_test] * max(self._num_required, available_bots_num)
Xixuan Wucb469512018-06-08 15:17:23 -0700452
Xixuan Wu19df5f32018-08-07 16:19:44 -0700453 def _get_test_specs(self, tests, available_bots, keyvals):
454 test_specs = []
455 for idx, test in enumerate(tests):
456 if idx < len(available_bots):
457 bot = available_bots[idx]
458 test_specs.append(self._create_test_spec(
459 test, keyvals, bot_id=bot['bot_id'],
460 dut_name=swarming_lib.get_task_dut_name(
461 bot['dimensions'])))
462 else:
463 test_specs.append(self._create_test_spec(test, keyvals))
464
465 return test_specs