blob: 1f0a2c911bac1f95fa0216e942a7b2c02baaafbd [file] [log] [blame]
Derek Beckett5fb683c2020-08-19 15:24:13 -07001# Lint as: python2, python3
Alex Miller0516e4c2013-06-03 18:07:48 -07002# Copyright (c) 2013 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
Allen Li606673f2017-02-21 18:41:13 -08006import collections
Richard Barnette6c2b70a2017-01-26 13:40:51 -08007import re
Dan Shi7279a5a2016-04-07 11:04:28 -07008import sys
Allen Liaaabda82017-02-21 18:27:38 -08009import warnings
Alex Miller0516e4c2013-06-03 18:07:48 -070010
11import common
Fang Dengaf30e7c2014-11-15 13:57:03 -080012from autotest_lib.server.cros import provision_actionables as actionables
Allen Li89711f72017-02-21 18:21:52 -080013from autotest_lib.utils import labellib
14from autotest_lib.utils.labellib import Key
Alex Miller0516e4c2013-06-03 18:07:48 -070015
16
17### Constants for label prefixes
Allen Li89711f72017-02-21 18:21:52 -080018CROS_VERSION_PREFIX = Key.CROS_VERSION
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070019CROS_ANDROID_VERSION_PREFIX = Key.CROS_ANDROID_VERSION
Allen Li89711f72017-02-21 18:21:52 -080020FW_RW_VERSION_PREFIX = Key.FIRMWARE_RW_VERSION
21FW_RO_VERSION_PREFIX = Key.FIRMWARE_RO_VERSION
Mary Ruthven7a3b15d2019-09-17 18:45:10 -070022FW_CR50_RW_VERSION_PREFIX = Key.FIRMWARE_CR50_RW_VERSION
Alex Miller0516e4c2013-06-03 18:07:48 -070023
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070024# So far the word cheets is only way to distinguish between ARC and Android
25# build.
Jacob Kopczynski92082ea2018-07-09 13:49:55 -070026_CROS_ANDROID_BUILD_REGEX = r'.+/cheets.*/P?([0-9]+|LATEST)'
Richard Barnette6c2b70a2017-01-26 13:40:51 -080027
Dan Shie44f9c02016-02-18 13:25:05 -080028# Special label to skip provision and run reset instead.
29SKIP_PROVISION = 'skip_provision'
30
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070031# Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning.
32CHEETS_SUFFIX = '-cheetsth'
33
Namyoon Woo5f894662019-11-15 15:23:23 -080034# ChromeOS image archive server address
35CROS_IMAGE_ARCHIVE = 'gs://chromeos-image-archive'
36
37# ChromeOS firmware branch directory name. %s is for a (base)board name.
38FW_BRANCH_GLOB = 'firmware-%s-[0-9]*.B-firmwarebranch'
Richard Barnette71854c72018-03-30 14:22:09 -070039
Allen Li606673f2017-02-21 18:41:13 -080040_Action = collections.namedtuple('_Action', 'name, value')
41
42
43def _get_label_action(str_label):
44 """Get action represented by the label.
45
46 This is used for determine actions to perform based on labels, for
47 example for provisioning or repair.
48
49 @param str_label: label string
50 @returns: _Action instance
51 """
52 try:
53 keyval_label = labellib.parse_keyval_label(str_label)
54 except ValueError:
55 return _Action(str_label, None)
56 else:
57 return _Action(keyval_label.key, keyval_label.value)
58
59
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000060### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080061def get_version_label_prefix(image):
62 """
63 Determine a version label prefix from a given image name.
64
65 Parses `image` to determine what kind of image it refers
66 to, and returns the corresponding version label prefix.
67
68 Known version label prefixes are:
69 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
70 These images have names like `cave-release/R57-9030.0.0`.
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070071 * `CROS_ANDROID_VERSION_PREFIX` for Chrome OS Android version strings.
72 These images have names like `git_nyc-arc/cheets_x86-user/3512523`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080073
74 @param image: The image name to be parsed.
75 @returns: A string that is the prefix of version labels for the type
76 of image identified by `image`.
77
78 """
Richard Barnette66eb19d2018-04-30 23:46:52 +000079 if re.match(_CROS_ANDROID_BUILD_REGEX, image, re.I):
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070080 return CROS_ANDROID_VERSION_PREFIX
Richard Barnette6c2b70a2017-01-26 13:40:51 -080081 else:
82 return CROS_VERSION_PREFIX
83
84
85def image_version_to_label(image):
86 """
87 Return a version label appropriate to the given image name.
88
89 The type of version label is as determined described for
90 `get_version_label_prefix()`, meaning the label will identify a
Richard Barnette17fa7c42018-04-26 00:33:11 +000091 CrOS or Android version.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080092
93 @param image: The image name to be parsed.
94 @returns: A string that is the appropriate label name.
95
96 """
97 return get_version_label_prefix(image) + ':' + image
98
99
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000100def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -0700101 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000102 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800103
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000104 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
105 @returns: A string that is the appropriate label name.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800106
107 """
Allen Liaaabda82017-02-21 18:27:38 -0800108 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
109 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
110 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800111
112
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000113def fwrw_version_to_label(image):
114 """
115 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700116
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000117 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
118 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700119
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000120 """
Allen Liaaabda82017-02-21 18:27:38 -0800121 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
122 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
123 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700124
125
Alex Miller1968edf2014-02-27 18:11:36 -0800126class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700127 """
Alex Miller1968edf2014-02-27 18:11:36 -0800128 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700129 """
Alex Miller1968edf2014-02-27 18:11:36 -0800130
Dan Shi7279a5a2016-04-07 11:04:28 -0700131 # A dictionary mapping labels to test names.
132 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700133
Dan Shi7279a5a2016-04-07 11:04:28 -0700134 # The name of this special task to be used in output.
135 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700136
Dan Shi7279a5a2016-04-07 11:04:28 -0700137 # Some special tasks require to run before others, e.g., ChromeOS image
138 # needs to be updated before firmware provision. List `_priorities` defines
139 # the order of each label prefix. An element with a smaller index has higher
140 # priority. Not listed ones have the lowest priority.
141 # This property should be overriden in subclass to define its own priorities
142 # across available label prefixes.
143 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800144
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000145
Alex Miller1968edf2014-02-27 18:11:36 -0800146 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000147 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800148 """
149 Returns True if the label is a label that we recognize as something we
150 know how to act on, given our _actions.
151
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000152 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800153 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800154 """
Allen Li95e930a2017-02-21 18:31:18 -0800155 action = _get_label_action(label)
156 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000157
Alex Miller1968edf2014-02-27 18:11:36 -0800158
159 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800160 def run_task_actions(cls, job, host, labels):
161 """
162 Run task actions on host that correspond to the labels.
163
164 Emits status lines for each run test, and INFO lines for each
165 skipped label.
166
167 @param job: A job object from a control file.
168 @param host: The host to run actions on.
169 @param labels: The list of job labels to work on.
170 @raises: SpecialTaskActionException if a test fails.
171 """
Allen Lid575f642017-05-23 13:07:28 -0700172 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800173 for label in unactionable:
174 job.record('INFO', None, cls.name,
175 "Can't %s label '%s'." % (cls.name, label))
176
Allen Lid575f642017-05-23 13:07:28 -0700177 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800178 success = action_item.execute(job=job, host=host, value=value)
179 if not success:
180 raise SpecialTaskActionException()
181
182
183 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700184 def _actions_and_values_iter(cls, labels):
185 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800186
Allen Lid575f642017-05-23 13:07:28 -0700187 @params: An iterable of label strings.
188 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800189 """
Allen Lid575f642017-05-23 13:07:28 -0700190 actionable = cls._filter_actionable_labels(labels)
191 keyval_mapping = labellib.LabelsMapping(actionable)
192 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
193 for name in sorted_names:
194 action_item = cls._actions[name]
195 value = keyval_mapping[name]
196 yield action_item, value
197
198
199 @classmethod
200 def _filter_unactionable_labels(cls, labels):
201 """
202 Return labels that we cannot act on.
203
204 @param labels: A list of strings of labels.
205 @returns: A set of unactionable labels
206 """
207 return {label for label in labels
208 if not (label == SKIP_PROVISION or cls.acts_on(label))}
209
210
211 @classmethod
212 def _filter_actionable_labels(cls, labels):
213 """
214 Return labels that we can act on.
215
216 @param labels: A list of strings of labels.
217 @returns: A set of actionable labels
218 """
219 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800220
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000221
Alex Milleraa772002014-04-10 17:51:21 -0700222 @classmethod
223 def partition(cls, labels):
224 """
225 Filter a list of labels into two sets: those labels that we know how to
226 act on and those that we don't know how to act on.
227
228 @param labels: A list of strings of labels.
229 @returns: A tuple where the first element is a set of unactionable
230 labels, and the second element is a set of the actionable
231 labels.
232 """
Allen Li5b623122017-02-22 17:42:02 -0800233 unactionable = set()
234 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700235
236 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800237 if label == SKIP_PROVISION:
238 # skip_provision is neither actionable or a capability label.
239 # It doesn't need any handling.
240 continue
241 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800242 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700243 else:
Allen Li5b623122017-02-22 17:42:02 -0800244 unactionable.add(label)
Rohit Makasanac4eacc92017-09-06 13:49:38 -0700245
Allen Li5b623122017-02-22 17:42:02 -0800246 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700247
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000248
Dan Shi7279a5a2016-04-07 11:04:28 -0700249 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700250 def _get_action_priority(cls, name):
251 """Return priority for the action with the given name."""
252 if name in cls._priorities:
253 return cls._priorities.index(name)
254 else:
Derek Beckett5fb683c2020-08-19 15:24:13 -0700255 return sys.maxsize
Dan Shi7279a5a2016-04-07 11:04:28 -0700256
257
Alex Miller1968edf2014-02-27 18:11:36 -0800258class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700259 """
Arowa Suliman66f00db2021-05-21 15:50:32 -0700260 Tests to verify that the DUT is in a known good state that we can run
Alex Miller1968edf2014-02-27 18:11:36 -0800261 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700262 """
Alex Miller1968edf2014-02-27 18:11:36 -0800263
264 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800265 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000266 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
267 # is stable in lab (destiny). The power_RPMTest failure led to reset job
268 # failure and that left dut in Repair Failed. Since the test will fail
269 # anyway due to the destiny lab issue, and test retry will retry the
270 # test in another DUT.
271 # This change temporarily disable the RPM check in reset job.
272 # Another way to do this is to remove rpm dependency from tests' control
273 # file. That will involve changes on multiple control files. This one
274 # line change here is a simple temporary fix.
275 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800276 }
277
278 name = 'verify'
279
280
281class Provision(_SpecialTaskAction):
282 """
283 Provisioning runs to change the configuration of the DUT from one state to
284 another. It will only be run on verified DUTs.
285 """
286
Dan Shi7279a5a2016-04-07 11:04:28 -0700287 # ChromeOS update must happen before firmware install, so the dut has the
288 # correct ChromeOS version label when firmware install occurs. The ChromeOS
289 # version label is used for firmware update to stage desired ChromeOS image
290 # on to the servo USB stick.
291 _priorities = [CROS_VERSION_PREFIX,
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700292 CROS_ANDROID_VERSION_PREFIX,
Dan Shi7279a5a2016-04-07 11:04:28 -0700293 FW_RO_VERSION_PREFIX,
Mary Ruthven7a3b15d2019-09-17 18:45:10 -0700294 FW_RW_VERSION_PREFIX,
295 FW_CR50_RW_VERSION_PREFIX]
Dan Shi7279a5a2016-04-07 11:04:28 -0700296
Alex Miller1968edf2014-02-27 18:11:36 -0800297 # TODO(milleral): http://crbug.com/249555
298 # Create some way to discover and register provisioning tests so that we
299 # don't need to hand-maintain a list of all of them.
300 _actions = {
Jae Hoon Kimd2b607a2020-09-11 16:22:00 -0700301 CROS_VERSION_PREFIX:
302 actionables.TestActionable(
303 'provision_QuickProvision',
304 extra_kwargs={
305 'disable_sysinfo': False,
306 'disable_before_test_sysinfo': False,
307 'disable_before_iteration_sysinfo': True,
308 'disable_after_test_sysinfo': True,
309 'disable_after_iteration_sysinfo': True
310 }),
311 CROS_ANDROID_VERSION_PREFIX:
312 actionables.TestActionable('provision_CheetsUpdate'),
313 FW_RO_VERSION_PREFIX:
314 actionables.TestActionable('provision_FirmwareUpdate'),
315 FW_RW_VERSION_PREFIX:
316 actionables.TestActionable('provision_FirmwareUpdate',
317 extra_kwargs={
318 'rw_only': True,
319 'tag': 'rw_only'
320 }),
321 FW_CR50_RW_VERSION_PREFIX:
322 actionables.TestActionable('provision_Cr50TOT')
Alex Miller1968edf2014-02-27 18:11:36 -0800323 }
324
325 name = 'provision'
326
327
328class Cleanup(_SpecialTaskAction):
329 """
330 Cleanup runs after a test fails to try and remove artifacts of tests and
Arowa Suliman66f00db2021-05-21 15:50:32 -0700331 ensure the DUT will be in a good state for the next test run.
Alex Miller1968edf2014-02-27 18:11:36 -0800332 """
333
334 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800335 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800336 }
337
338 name = 'cleanup'
339
340
Allen Li28c303c2019-01-04 16:56:26 -0800341# TODO(ayatane): This class doesn't do anything. It's safe to remove
342# after all references to it are removed (after some buffer time to be
343# safe, like a few release cycles).
Alex Miller1968edf2014-02-27 18:11:36 -0800344class Repair(_SpecialTaskAction):
345 """
346 Repair runs when one of the other special tasks fails. It should be able
347 to take a component of the DUT that's in an unknown state and restore it to
348 a good state.
349 """
350
351 _actions = {
352 }
353
354 name = 'repair'
355
356
Alex Milleraa772002014-04-10 17:51:21 -0700357# TODO(milleral): crbug.com/364273
358# Label doesn't really mean label in this context. We're putting things into
359# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
360# doing that.
361def is_for_special_action(label):
362 """
363 If any special task handles the label specially, then we're using the label
364 to communicate that we want an action, and not as an actual dependency that
365 the test has.
366
367 @param label: A string label name.
368 @return True if any special task handles this label specially,
369 False if no special task handles this label.
370 """
371 return (Verify.acts_on(label) or
372 Provision.acts_on(label) or
373 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800374 Repair.acts_on(label) or
375 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700376
377
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000378def join(provision_type, provision_value):
379 """
380 Combine the provision type and value into the label name.
381
382 @param provision_type: One of the constants that are the label prefixes.
383 @param provision_value: A string of the value for this provision type.
384 @returns: A string that is the label name for this (type, value) pair.
385
386 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
387 'cros-version:lumpy-release/R27-3773.0.0'
388
389 """
390 return '%s:%s' % (provision_type, provision_value)
391
392
Alex Miller667b5f22014-02-28 15:33:39 -0800393class SpecialTaskActionException(Exception):
394 """
395 Exception raised when a special task fails to successfully run a test that
396 is required.
397
398 This is also a literally meaningless exception. It's always just discarded.
399 """