blob: 3006f6cc30db7f248053cfc54716807829ef7913 [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
Alex Miller0516e4c2013-06-03 18:07:48 -070021
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070022# So far the word cheets is only way to distinguish between ARC and Android
23# build.
Jacob Kopczynski92082ea2018-07-09 13:49:55 -070024_CROS_ANDROID_BUILD_REGEX = r'.+/cheets.*/P?([0-9]+|LATEST)'
Richard Barnette6c2b70a2017-01-26 13:40:51 -080025
Dan Shie44f9c02016-02-18 13:25:05 -080026# Special label to skip provision and run reset instead.
27SKIP_PROVISION = 'skip_provision'
28
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070029# Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning.
30CHEETS_SUFFIX = '-cheetsth'
31
Richard Barnette71854c72018-03-30 14:22:09 -070032
Allen Li606673f2017-02-21 18:41:13 -080033_Action = collections.namedtuple('_Action', 'name, value')
34
35
36def _get_label_action(str_label):
37 """Get action represented by the label.
38
39 This is used for determine actions to perform based on labels, for
40 example for provisioning or repair.
41
42 @param str_label: label string
43 @returns: _Action instance
44 """
45 try:
46 keyval_label = labellib.parse_keyval_label(str_label)
47 except ValueError:
48 return _Action(str_label, None)
49 else:
50 return _Action(keyval_label.key, keyval_label.value)
51
52
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000053### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080054def get_version_label_prefix(image):
55 """
56 Determine a version label prefix from a given image name.
57
58 Parses `image` to determine what kind of image it refers
59 to, and returns the corresponding version label prefix.
60
61 Known version label prefixes are:
62 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
63 These images have names like `cave-release/R57-9030.0.0`.
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070064 * `CROS_ANDROID_VERSION_PREFIX` for Chrome OS Android version strings.
65 These images have names like `git_nyc-arc/cheets_x86-user/3512523`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080066
67 @param image: The image name to be parsed.
68 @returns: A string that is the prefix of version labels for the type
69 of image identified by `image`.
70
71 """
Richard Barnette66eb19d2018-04-30 23:46:52 +000072 if re.match(_CROS_ANDROID_BUILD_REGEX, image, re.I):
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070073 return CROS_ANDROID_VERSION_PREFIX
Richard Barnette6c2b70a2017-01-26 13:40:51 -080074 else:
75 return CROS_VERSION_PREFIX
76
77
78def image_version_to_label(image):
79 """
80 Return a version label appropriate to the given image name.
81
82 The type of version label is as determined described for
83 `get_version_label_prefix()`, meaning the label will identify a
Richard Barnette17fa7c42018-04-26 00:33:11 +000084 CrOS or Android version.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080085
86 @param image: The image name to be parsed.
87 @returns: A string that is the appropriate label name.
88
89 """
90 return get_version_label_prefix(image) + ':' + image
91
92
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000093def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -070094 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000095 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080096
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000097 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
98 @returns: A string that is the appropriate label name.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080099
100 """
Allen Liaaabda82017-02-21 18:27:38 -0800101 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
102 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
103 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800104
105
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000106def fwrw_version_to_label(image):
107 """
108 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700109
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000110 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
111 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700112
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000113 """
Allen Liaaabda82017-02-21 18:27:38 -0800114 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
115 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
116 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700117
118
Alex Miller1968edf2014-02-27 18:11:36 -0800119class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700120 """
Alex Miller1968edf2014-02-27 18:11:36 -0800121 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700122 """
Alex Miller1968edf2014-02-27 18:11:36 -0800123
Dan Shi7279a5a2016-04-07 11:04:28 -0700124 # A dictionary mapping labels to test names.
125 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700126
Dan Shi7279a5a2016-04-07 11:04:28 -0700127 # The name of this special task to be used in output.
128 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700129
Dan Shi7279a5a2016-04-07 11:04:28 -0700130 # Some special tasks require to run before others, e.g., ChromeOS image
131 # needs to be updated before firmware provision. List `_priorities` defines
132 # the order of each label prefix. An element with a smaller index has higher
133 # priority. Not listed ones have the lowest priority.
134 # This property should be overriden in subclass to define its own priorities
135 # across available label prefixes.
136 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800137
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000138
Alex Miller1968edf2014-02-27 18:11:36 -0800139 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000140 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800141 """
142 Returns True if the label is a label that we recognize as something we
143 know how to act on, given our _actions.
144
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000145 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800146 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800147 """
Allen Li95e930a2017-02-21 18:31:18 -0800148 action = _get_label_action(label)
149 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000150
Alex Miller1968edf2014-02-27 18:11:36 -0800151
152 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800153 def run_task_actions(cls, job, host, labels):
154 """
155 Run task actions on host that correspond to the labels.
156
157 Emits status lines for each run test, and INFO lines for each
158 skipped label.
159
160 @param job: A job object from a control file.
161 @param host: The host to run actions on.
162 @param labels: The list of job labels to work on.
163 @raises: SpecialTaskActionException if a test fails.
164 """
Allen Lid575f642017-05-23 13:07:28 -0700165 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800166 for label in unactionable:
167 job.record('INFO', None, cls.name,
168 "Can't %s label '%s'." % (cls.name, label))
169
Allen Lid575f642017-05-23 13:07:28 -0700170 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800171 success = action_item.execute(job=job, host=host, value=value)
172 if not success:
173 raise SpecialTaskActionException()
174
175
176 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700177 def _actions_and_values_iter(cls, labels):
178 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800179
Allen Lid575f642017-05-23 13:07:28 -0700180 @params: An iterable of label strings.
181 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800182 """
Allen Lid575f642017-05-23 13:07:28 -0700183 actionable = cls._filter_actionable_labels(labels)
184 keyval_mapping = labellib.LabelsMapping(actionable)
185 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
186 for name in sorted_names:
187 action_item = cls._actions[name]
188 value = keyval_mapping[name]
189 yield action_item, value
190
191
192 @classmethod
193 def _filter_unactionable_labels(cls, labels):
194 """
195 Return labels that we cannot act on.
196
197 @param labels: A list of strings of labels.
198 @returns: A set of unactionable labels
199 """
200 return {label for label in labels
201 if not (label == SKIP_PROVISION or cls.acts_on(label))}
202
203
204 @classmethod
205 def _filter_actionable_labels(cls, labels):
206 """
207 Return labels that we can act on.
208
209 @param labels: A list of strings of labels.
210 @returns: A set of actionable labels
211 """
212 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800213
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000214
Alex Milleraa772002014-04-10 17:51:21 -0700215 @classmethod
216 def partition(cls, labels):
217 """
218 Filter a list of labels into two sets: those labels that we know how to
219 act on and those that we don't know how to act on.
220
221 @param labels: A list of strings of labels.
222 @returns: A tuple where the first element is a set of unactionable
223 labels, and the second element is a set of the actionable
224 labels.
225 """
Allen Li5b623122017-02-22 17:42:02 -0800226 unactionable = set()
227 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700228
229 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800230 if label == SKIP_PROVISION:
231 # skip_provision is neither actionable or a capability label.
232 # It doesn't need any handling.
233 continue
234 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800235 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700236 else:
Allen Li5b623122017-02-22 17:42:02 -0800237 unactionable.add(label)
Rohit Makasanac4eacc92017-09-06 13:49:38 -0700238
Allen Li5b623122017-02-22 17:42:02 -0800239 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700240
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000241
Dan Shi7279a5a2016-04-07 11:04:28 -0700242 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700243 def _get_action_priority(cls, name):
244 """Return priority for the action with the given name."""
245 if name in cls._priorities:
246 return cls._priorities.index(name)
247 else:
248 return sys.maxint
Dan Shi7279a5a2016-04-07 11:04:28 -0700249
250
Alex Miller1968edf2014-02-27 18:11:36 -0800251class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700252 """
Alex Miller1968edf2014-02-27 18:11:36 -0800253 Tests to verify that the DUT is in a sane, known good state that we can run
254 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700255 """
Alex Miller1968edf2014-02-27 18:11:36 -0800256
257 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800258 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000259 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
260 # is stable in lab (destiny). The power_RPMTest failure led to reset job
261 # failure and that left dut in Repair Failed. Since the test will fail
262 # anyway due to the destiny lab issue, and test retry will retry the
263 # test in another DUT.
264 # This change temporarily disable the RPM check in reset job.
265 # Another way to do this is to remove rpm dependency from tests' control
266 # file. That will involve changes on multiple control files. This one
267 # line change here is a simple temporary fix.
268 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800269 }
270
271 name = 'verify'
272
273
274class Provision(_SpecialTaskAction):
275 """
276 Provisioning runs to change the configuration of the DUT from one state to
277 another. It will only be run on verified DUTs.
278 """
279
Dan Shi7279a5a2016-04-07 11:04:28 -0700280 # ChromeOS update must happen before firmware install, so the dut has the
281 # correct ChromeOS version label when firmware install occurs. The ChromeOS
282 # version label is used for firmware update to stage desired ChromeOS image
283 # on to the servo USB stick.
284 _priorities = [CROS_VERSION_PREFIX,
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700285 CROS_ANDROID_VERSION_PREFIX,
Dan Shi7279a5a2016-04-07 11:04:28 -0700286 FW_RO_VERSION_PREFIX,
287 FW_RW_VERSION_PREFIX]
288
Alex Miller1968edf2014-02-27 18:11:36 -0800289 # TODO(milleral): http://crbug.com/249555
290 # Create some way to discover and register provisioning tests so that we
291 # don't need to hand-maintain a list of all of them.
292 _actions = {
Fang Deng9d548742015-02-03 11:35:02 -0800293 CROS_VERSION_PREFIX: actionables.TestActionable(
294 'provision_AutoUpdate',
295 extra_kwargs={'disable_sysinfo': False,
296 'disable_before_test_sysinfo': False,
297 'disable_before_iteration_sysinfo': True,
298 'disable_after_test_sysinfo': True,
299 'disable_after_iteration_sysinfo': True}),
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700300 CROS_ANDROID_VERSION_PREFIX : actionables.TestActionable(
Rohit Makasana37f5cf02017-06-08 17:21:25 -0700301 'provision_CheetsUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800302 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800303 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800304 FW_RW_VERSION_PREFIX: actionables.TestActionable(
305 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700306 extra_kwargs={'rw_only': True,
307 'tag': 'rw_only'}),
Alex Miller1968edf2014-02-27 18:11:36 -0800308 }
309
310 name = 'provision'
311
312
313class Cleanup(_SpecialTaskAction):
314 """
315 Cleanup runs after a test fails to try and remove artifacts of tests and
316 ensure the DUT will be in a sane state for the next test run.
317 """
318
319 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800320 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800321 }
322
323 name = 'cleanup'
324
325
326class Repair(_SpecialTaskAction):
327 """
328 Repair runs when one of the other special tasks fails. It should be able
329 to take a component of the DUT that's in an unknown state and restore it to
330 a good state.
331 """
332
333 _actions = {
334 }
335
336 name = 'repair'
337
338
Alex Milleraa772002014-04-10 17:51:21 -0700339# TODO(milleral): crbug.com/364273
340# Label doesn't really mean label in this context. We're putting things into
341# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
342# doing that.
343def is_for_special_action(label):
344 """
345 If any special task handles the label specially, then we're using the label
346 to communicate that we want an action, and not as an actual dependency that
347 the test has.
348
349 @param label: A string label name.
350 @return True if any special task handles this label specially,
351 False if no special task handles this label.
352 """
353 return (Verify.acts_on(label) or
354 Provision.acts_on(label) or
355 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800356 Repair.acts_on(label) or
357 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700358
359
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000360def join(provision_type, provision_value):
361 """
362 Combine the provision type and value into the label name.
363
364 @param provision_type: One of the constants that are the label prefixes.
365 @param provision_value: A string of the value for this provision type.
366 @returns: A string that is the label name for this (type, value) pair.
367
368 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
369 'cros-version:lumpy-release/R27-3773.0.0'
370
371 """
372 return '%s:%s' % (provision_type, provision_value)
373
374
Alex Miller667b5f22014-02-28 15:33:39 -0800375class SpecialTaskActionException(Exception):
376 """
377 Exception raised when a special task fails to successfully run a test that
378 is required.
379
380 This is also a literally meaningless exception. It's always just discarded.
381 """