blob: 46d0b1f5bdeb2fe9b5334cc3132e2703758be5e4 [file] [log] [blame]
Alex Miller0516e4c2013-06-03 18:07:48 -07001# Copyright (c) 2013 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
Allen Li606673f2017-02-21 18:41:13 -08005import collections
Richard Barnette6c2b70a2017-01-26 13:40:51 -08006import re
Dan Shi7279a5a2016-04-07 11:04:28 -07007import sys
Allen Liaaabda82017-02-21 18:27:38 -08008import warnings
Alex Miller0516e4c2013-06-03 18:07:48 -07009
10import common
Fang Dengaf30e7c2014-11-15 13:57:03 -080011from autotest_lib.server.cros import provision_actionables as actionables
Allen Li89711f72017-02-21 18:21:52 -080012from autotest_lib.utils import labellib
13from autotest_lib.utils.labellib import Key
Alex Miller0516e4c2013-06-03 18:07:48 -070014
15
16### Constants for label prefixes
Allen Li89711f72017-02-21 18:21:52 -080017CROS_VERSION_PREFIX = Key.CROS_VERSION
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070018CROS_ANDROID_VERSION_PREFIX = Key.CROS_ANDROID_VERSION
Allen Li89711f72017-02-21 18:21:52 -080019FW_RW_VERSION_PREFIX = Key.FIRMWARE_RW_VERSION
20FW_RO_VERSION_PREFIX = Key.FIRMWARE_RO_VERSION
Mary Ruthven7a3b15d2019-09-17 18:45:10 -070021FW_CR50_RW_VERSION_PREFIX = Key.FIRMWARE_CR50_RW_VERSION
Alex Miller0516e4c2013-06-03 18:07:48 -070022
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070023# So far the word cheets is only way to distinguish between ARC and Android
24# build.
Jacob Kopczynski92082ea2018-07-09 13:49:55 -070025_CROS_ANDROID_BUILD_REGEX = r'.+/cheets.*/P?([0-9]+|LATEST)'
Richard Barnette6c2b70a2017-01-26 13:40:51 -080026
Dan Shie44f9c02016-02-18 13:25:05 -080027# Special label to skip provision and run reset instead.
28SKIP_PROVISION = 'skip_provision'
29
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070030# Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning.
31CHEETS_SUFFIX = '-cheetsth'
32
Namyoon Woo5f894662019-11-15 15:23:23 -080033# ChromeOS image archive server address
34CROS_IMAGE_ARCHIVE = 'gs://chromeos-image-archive'
35
36# ChromeOS firmware branch directory name. %s is for a (base)board name.
37FW_BRANCH_GLOB = 'firmware-%s-[0-9]*.B-firmwarebranch'
Richard Barnette71854c72018-03-30 14:22:09 -070038
Allen Li606673f2017-02-21 18:41:13 -080039_Action = collections.namedtuple('_Action', 'name, value')
40
41
42def _get_label_action(str_label):
43 """Get action represented by the label.
44
45 This is used for determine actions to perform based on labels, for
46 example for provisioning or repair.
47
48 @param str_label: label string
49 @returns: _Action instance
50 """
51 try:
52 keyval_label = labellib.parse_keyval_label(str_label)
53 except ValueError:
54 return _Action(str_label, None)
55 else:
56 return _Action(keyval_label.key, keyval_label.value)
57
58
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000059### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080060def get_version_label_prefix(image):
61 """
62 Determine a version label prefix from a given image name.
63
64 Parses `image` to determine what kind of image it refers
65 to, and returns the corresponding version label prefix.
66
67 Known version label prefixes are:
68 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
69 These images have names like `cave-release/R57-9030.0.0`.
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070070 * `CROS_ANDROID_VERSION_PREFIX` for Chrome OS Android version strings.
71 These images have names like `git_nyc-arc/cheets_x86-user/3512523`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080072
73 @param image: The image name to be parsed.
74 @returns: A string that is the prefix of version labels for the type
75 of image identified by `image`.
76
77 """
Richard Barnette66eb19d2018-04-30 23:46:52 +000078 if re.match(_CROS_ANDROID_BUILD_REGEX, image, re.I):
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070079 return CROS_ANDROID_VERSION_PREFIX
Richard Barnette6c2b70a2017-01-26 13:40:51 -080080 else:
81 return CROS_VERSION_PREFIX
82
83
84def image_version_to_label(image):
85 """
86 Return a version label appropriate to the given image name.
87
88 The type of version label is as determined described for
89 `get_version_label_prefix()`, meaning the label will identify a
Richard Barnette17fa7c42018-04-26 00:33:11 +000090 CrOS or Android version.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080091
92 @param image: The image name to be parsed.
93 @returns: A string that is the appropriate label name.
94
95 """
96 return get_version_label_prefix(image) + ':' + image
97
98
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000099def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -0700100 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000101 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800102
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000103 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
104 @returns: A string that is the appropriate label name.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800105
106 """
Allen Liaaabda82017-02-21 18:27:38 -0800107 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
108 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
109 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800110
111
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000112def fwrw_version_to_label(image):
113 """
114 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700115
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000116 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
117 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700118
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000119 """
Allen Liaaabda82017-02-21 18:27:38 -0800120 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
121 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
122 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700123
124
Alex Miller1968edf2014-02-27 18:11:36 -0800125class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700126 """
Alex Miller1968edf2014-02-27 18:11:36 -0800127 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700128 """
Alex Miller1968edf2014-02-27 18:11:36 -0800129
Dan Shi7279a5a2016-04-07 11:04:28 -0700130 # A dictionary mapping labels to test names.
131 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700132
Dan Shi7279a5a2016-04-07 11:04:28 -0700133 # The name of this special task to be used in output.
134 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700135
Dan Shi7279a5a2016-04-07 11:04:28 -0700136 # Some special tasks require to run before others, e.g., ChromeOS image
137 # needs to be updated before firmware provision. List `_priorities` defines
138 # the order of each label prefix. An element with a smaller index has higher
139 # priority. Not listed ones have the lowest priority.
140 # This property should be overriden in subclass to define its own priorities
141 # across available label prefixes.
142 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800143
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000144
Alex Miller1968edf2014-02-27 18:11:36 -0800145 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000146 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800147 """
148 Returns True if the label is a label that we recognize as something we
149 know how to act on, given our _actions.
150
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000151 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800152 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800153 """
Allen Li95e930a2017-02-21 18:31:18 -0800154 action = _get_label_action(label)
155 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000156
Alex Miller1968edf2014-02-27 18:11:36 -0800157
158 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800159 def run_task_actions(cls, job, host, labels):
160 """
161 Run task actions on host that correspond to the labels.
162
163 Emits status lines for each run test, and INFO lines for each
164 skipped label.
165
166 @param job: A job object from a control file.
167 @param host: The host to run actions on.
168 @param labels: The list of job labels to work on.
169 @raises: SpecialTaskActionException if a test fails.
170 """
Allen Lid575f642017-05-23 13:07:28 -0700171 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800172 for label in unactionable:
173 job.record('INFO', None, cls.name,
174 "Can't %s label '%s'." % (cls.name, label))
175
Allen Lid575f642017-05-23 13:07:28 -0700176 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800177 success = action_item.execute(job=job, host=host, value=value)
178 if not success:
179 raise SpecialTaskActionException()
180
181
182 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700183 def _actions_and_values_iter(cls, labels):
184 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800185
Allen Lid575f642017-05-23 13:07:28 -0700186 @params: An iterable of label strings.
187 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800188 """
Allen Lid575f642017-05-23 13:07:28 -0700189 actionable = cls._filter_actionable_labels(labels)
190 keyval_mapping = labellib.LabelsMapping(actionable)
191 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
192 for name in sorted_names:
193 action_item = cls._actions[name]
194 value = keyval_mapping[name]
195 yield action_item, value
196
197
198 @classmethod
199 def _filter_unactionable_labels(cls, labels):
200 """
201 Return labels that we cannot act on.
202
203 @param labels: A list of strings of labels.
204 @returns: A set of unactionable labels
205 """
206 return {label for label in labels
207 if not (label == SKIP_PROVISION or cls.acts_on(label))}
208
209
210 @classmethod
211 def _filter_actionable_labels(cls, labels):
212 """
213 Return labels that we can act on.
214
215 @param labels: A list of strings of labels.
216 @returns: A set of actionable labels
217 """
218 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800219
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000220
Alex Milleraa772002014-04-10 17:51:21 -0700221 @classmethod
222 def partition(cls, labels):
223 """
224 Filter a list of labels into two sets: those labels that we know how to
225 act on and those that we don't know how to act on.
226
227 @param labels: A list of strings of labels.
228 @returns: A tuple where the first element is a set of unactionable
229 labels, and the second element is a set of the actionable
230 labels.
231 """
Allen Li5b623122017-02-22 17:42:02 -0800232 unactionable = set()
233 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700234
235 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800236 if label == SKIP_PROVISION:
237 # skip_provision is neither actionable or a capability label.
238 # It doesn't need any handling.
239 continue
240 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800241 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700242 else:
Allen Li5b623122017-02-22 17:42:02 -0800243 unactionable.add(label)
Rohit Makasanac4eacc92017-09-06 13:49:38 -0700244
Allen Li5b623122017-02-22 17:42:02 -0800245 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700246
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000247
Dan Shi7279a5a2016-04-07 11:04:28 -0700248 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700249 def _get_action_priority(cls, name):
250 """Return priority for the action with the given name."""
251 if name in cls._priorities:
252 return cls._priorities.index(name)
253 else:
254 return sys.maxint
Dan Shi7279a5a2016-04-07 11:04:28 -0700255
256
Alex Miller1968edf2014-02-27 18:11:36 -0800257class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700258 """
Alex Miller1968edf2014-02-27 18:11:36 -0800259 Tests to verify that the DUT is in a sane, known good state that we can run
260 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700261 """
Alex Miller1968edf2014-02-27 18:11:36 -0800262
263 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800264 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000265 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
266 # is stable in lab (destiny). The power_RPMTest failure led to reset job
267 # failure and that left dut in Repair Failed. Since the test will fail
268 # anyway due to the destiny lab issue, and test retry will retry the
269 # test in another DUT.
270 # This change temporarily disable the RPM check in reset job.
271 # Another way to do this is to remove rpm dependency from tests' control
272 # file. That will involve changes on multiple control files. This one
273 # line change here is a simple temporary fix.
274 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800275 }
276
277 name = 'verify'
278
279
280class Provision(_SpecialTaskAction):
281 """
282 Provisioning runs to change the configuration of the DUT from one state to
283 another. It will only be run on verified DUTs.
284 """
285
Dan Shi7279a5a2016-04-07 11:04:28 -0700286 # ChromeOS update must happen before firmware install, so the dut has the
287 # correct ChromeOS version label when firmware install occurs. The ChromeOS
288 # version label is used for firmware update to stage desired ChromeOS image
289 # on to the servo USB stick.
290 _priorities = [CROS_VERSION_PREFIX,
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700291 CROS_ANDROID_VERSION_PREFIX,
Dan Shi7279a5a2016-04-07 11:04:28 -0700292 FW_RO_VERSION_PREFIX,
Mary Ruthven7a3b15d2019-09-17 18:45:10 -0700293 FW_RW_VERSION_PREFIX,
294 FW_CR50_RW_VERSION_PREFIX]
Dan Shi7279a5a2016-04-07 11:04:28 -0700295
Alex Miller1968edf2014-02-27 18:11:36 -0800296 # TODO(milleral): http://crbug.com/249555
297 # Create some way to discover and register provisioning tests so that we
298 # don't need to hand-maintain a list of all of them.
299 _actions = {
Fang Deng9d548742015-02-03 11:35:02 -0800300 CROS_VERSION_PREFIX: actionables.TestActionable(
301 'provision_AutoUpdate',
302 extra_kwargs={'disable_sysinfo': False,
303 'disable_before_test_sysinfo': False,
304 'disable_before_iteration_sysinfo': True,
305 'disable_after_test_sysinfo': True,
306 'disable_after_iteration_sysinfo': True}),
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700307 CROS_ANDROID_VERSION_PREFIX : actionables.TestActionable(
Rohit Makasana37f5cf02017-06-08 17:21:25 -0700308 'provision_CheetsUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800309 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800310 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800311 FW_RW_VERSION_PREFIX: actionables.TestActionable(
312 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700313 extra_kwargs={'rw_only': True,
314 'tag': 'rw_only'}),
Mary Ruthven7a3b15d2019-09-17 18:45:10 -0700315 FW_CR50_RW_VERSION_PREFIX: actionables.TestActionable(
316 'provision_Cr50TOT')
Alex Miller1968edf2014-02-27 18:11:36 -0800317 }
318
319 name = 'provision'
320
321
322class Cleanup(_SpecialTaskAction):
323 """
324 Cleanup runs after a test fails to try and remove artifacts of tests and
325 ensure the DUT will be in a sane state for the next test run.
326 """
327
328 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800329 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800330 }
331
332 name = 'cleanup'
333
334
Allen Li28c303c2019-01-04 16:56:26 -0800335# TODO(ayatane): This class doesn't do anything. It's safe to remove
336# after all references to it are removed (after some buffer time to be
337# safe, like a few release cycles).
Alex Miller1968edf2014-02-27 18:11:36 -0800338class Repair(_SpecialTaskAction):
339 """
340 Repair runs when one of the other special tasks fails. It should be able
341 to take a component of the DUT that's in an unknown state and restore it to
342 a good state.
343 """
344
345 _actions = {
346 }
347
348 name = 'repair'
349
350
Alex Milleraa772002014-04-10 17:51:21 -0700351# TODO(milleral): crbug.com/364273
352# Label doesn't really mean label in this context. We're putting things into
353# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
354# doing that.
355def is_for_special_action(label):
356 """
357 If any special task handles the label specially, then we're using the label
358 to communicate that we want an action, and not as an actual dependency that
359 the test has.
360
361 @param label: A string label name.
362 @return True if any special task handles this label specially,
363 False if no special task handles this label.
364 """
365 return (Verify.acts_on(label) or
366 Provision.acts_on(label) or
367 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800368 Repair.acts_on(label) or
369 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700370
371
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000372def join(provision_type, provision_value):
373 """
374 Combine the provision type and value into the label name.
375
376 @param provision_type: One of the constants that are the label prefixes.
377 @param provision_value: A string of the value for this provision type.
378 @returns: A string that is the label name for this (type, value) pair.
379
380 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
381 'cros-version:lumpy-release/R27-3773.0.0'
382
383 """
384 return '%s:%s' % (provision_type, provision_value)
385
386
Alex Miller667b5f22014-02-28 15:33:39 -0800387class SpecialTaskActionException(Exception):
388 """
389 Exception raised when a special task fails to successfully run a test that
390 is required.
391
392 This is also a literally meaningless exception. It's always just discarded.
393 """