blob: 70de1490b7a668b2495c685b50dbe5601169ebbf [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
18ANDROID_BUILD_VERSION_PREFIX = Key.ANDROID_BUILD_VERSION
19TESTBED_BUILD_VERSION_PREFIX = Key.TESTBED_VERSION
20FW_RW_VERSION_PREFIX = Key.FIRMWARE_RW_VERSION
21FW_RO_VERSION_PREFIX = Key.FIRMWARE_RO_VERSION
Alex Miller0516e4c2013-06-03 18:07:48 -070022
Richard Barnette6c2b70a2017-01-26 13:40:51 -080023_ANDROID_BUILD_REGEX = r'.+/.+/P?([0-9]+|LATEST)'
24_ANDROID_TESTBED_BUILD_REGEX = _ANDROID_BUILD_REGEX + '(,|(#[0-9]+))'
25
Dan Shie44f9c02016-02-18 13:25:05 -080026# Special label to skip provision and run reset instead.
27SKIP_PROVISION = 'skip_provision'
28
Chris Sosae92399e2015-04-24 11:32:59 -070029# Default number of provisions attempts to try if we believe the devserver is
30# flaky.
31FLAKY_DEVSERVER_ATTEMPTS = 2
32
Alex Miller0516e4c2013-06-03 18:07:48 -070033
Allen Li606673f2017-02-21 18:41:13 -080034_Action = collections.namedtuple('_Action', 'name, value')
35
36
37def _get_label_action(str_label):
38 """Get action represented by the label.
39
40 This is used for determine actions to perform based on labels, for
41 example for provisioning or repair.
42
43 @param str_label: label string
44 @returns: _Action instance
45 """
46 try:
47 keyval_label = labellib.parse_keyval_label(str_label)
48 except ValueError:
49 return _Action(str_label, None)
50 else:
51 return _Action(keyval_label.key, keyval_label.value)
52
53
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000054### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080055def get_version_label_prefix(image):
56 """
57 Determine a version label prefix from a given image name.
58
59 Parses `image` to determine what kind of image it refers
60 to, and returns the corresponding version label prefix.
61
62 Known version label prefixes are:
63 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
64 These images have names like `cave-release/R57-9030.0.0`.
65 * `ANDROID_BUILD_VERSION_PREFIX` for Android build versions
66 These images have names like
67 `git_mnc-release/shamu-userdebug/2457013`.
68 * `TESTBED_BUILD_VERSION_PREFIX` for Android testbed version
69 specifications. These are either comma separated lists of
70 Android versions, or an Android version with a suffix like
71 '#2', indicating two devices running the given build.
72
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 """
78 if re.match(_ANDROID_TESTBED_BUILD_REGEX, image, re.I):
79 return TESTBED_BUILD_VERSION_PREFIX
80 elif re.match(_ANDROID_BUILD_REGEX, image, re.I):
81 return ANDROID_BUILD_VERSION_PREFIX
82 else:
83 return CROS_VERSION_PREFIX
84
85
86def image_version_to_label(image):
87 """
88 Return a version label appropriate to the given image name.
89
90 The type of version label is as determined described for
91 `get_version_label_prefix()`, meaning the label will identify a
92 CrOS, Android, or Testbed version.
93
94 @param image: The image name to be parsed.
95 @returns: A string that is the appropriate label name.
96
97 """
98 return get_version_label_prefix(image) + ':' + image
99
100
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000101def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -0700102 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000103 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800104
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000105 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
106 @returns: A string that is the appropriate label name.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800107
108 """
Allen Liaaabda82017-02-21 18:27:38 -0800109 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
110 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
111 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800112
113
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000114def fwrw_version_to_label(image):
115 """
116 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700117
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000118 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
119 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700120
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000121 """
Allen Liaaabda82017-02-21 18:27:38 -0800122 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
123 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
124 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700125
126
Alex Miller1968edf2014-02-27 18:11:36 -0800127class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700128 """
Alex Miller1968edf2014-02-27 18:11:36 -0800129 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700130 """
Alex Miller1968edf2014-02-27 18:11:36 -0800131
Dan Shi7279a5a2016-04-07 11:04:28 -0700132 # A dictionary mapping labels to test names.
133 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700134
Dan Shi7279a5a2016-04-07 11:04:28 -0700135 # The name of this special task to be used in output.
136 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700137
Dan Shi7279a5a2016-04-07 11:04:28 -0700138 # Some special tasks require to run before others, e.g., ChromeOS image
139 # needs to be updated before firmware provision. List `_priorities` defines
140 # the order of each label prefix. An element with a smaller index has higher
141 # priority. Not listed ones have the lowest priority.
142 # This property should be overriden in subclass to define its own priorities
143 # across available label prefixes.
144 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800145
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000146
Alex Miller1968edf2014-02-27 18:11:36 -0800147 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000148 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800149 """
150 Returns True if the label is a label that we recognize as something we
151 know how to act on, given our _actions.
152
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000153 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800154 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800155 """
Allen Li95e930a2017-02-21 18:31:18 -0800156 action = _get_label_action(label)
157 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000158
Alex Miller1968edf2014-02-27 18:11:36 -0800159
160 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800161 def run_task_actions(cls, job, host, labels):
162 """
163 Run task actions on host that correspond to the labels.
164
165 Emits status lines for each run test, and INFO lines for each
166 skipped label.
167
168 @param job: A job object from a control file.
169 @param host: The host to run actions on.
170 @param labels: The list of job labels to work on.
171 @raises: SpecialTaskActionException if a test fails.
172 """
Allen Lid575f642017-05-23 13:07:28 -0700173 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800174 for label in unactionable:
175 job.record('INFO', None, cls.name,
176 "Can't %s label '%s'." % (cls.name, label))
177
Allen Lid575f642017-05-23 13:07:28 -0700178 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800179 success = action_item.execute(job=job, host=host, value=value)
180 if not success:
181 raise SpecialTaskActionException()
182
183
184 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700185 def _actions_and_values_iter(cls, labels):
186 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800187
Allen Lid575f642017-05-23 13:07:28 -0700188 @params: An iterable of label strings.
189 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800190 """
Allen Lid575f642017-05-23 13:07:28 -0700191 actionable = cls._filter_actionable_labels(labels)
192 keyval_mapping = labellib.LabelsMapping(actionable)
193 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
194 for name in sorted_names:
195 action_item = cls._actions[name]
196 value = keyval_mapping[name]
197 yield action_item, value
198
199
200 @classmethod
201 def _filter_unactionable_labels(cls, labels):
202 """
203 Return labels that we cannot act on.
204
205 @param labels: A list of strings of labels.
206 @returns: A set of unactionable labels
207 """
208 return {label for label in labels
209 if not (label == SKIP_PROVISION or cls.acts_on(label))}
210
211
212 @classmethod
213 def _filter_actionable_labels(cls, labels):
214 """
215 Return labels that we can act on.
216
217 @param labels: A list of strings of labels.
218 @returns: A set of actionable labels
219 """
220 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800221
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000222
Alex Milleraa772002014-04-10 17:51:21 -0700223 @classmethod
224 def partition(cls, labels):
225 """
226 Filter a list of labels into two sets: those labels that we know how to
227 act on and those that we don't know how to act on.
228
229 @param labels: A list of strings of labels.
230 @returns: A tuple where the first element is a set of unactionable
231 labels, and the second element is a set of the actionable
232 labels.
233 """
Allen Li5b623122017-02-22 17:42:02 -0800234 unactionable = set()
235 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700236
237 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800238 if label == SKIP_PROVISION:
239 # skip_provision is neither actionable or a capability label.
240 # It doesn't need any handling.
241 continue
242 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800243 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700244 else:
Allen Li5b623122017-02-22 17:42:02 -0800245 unactionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700246
Allen Li5b623122017-02-22 17:42:02 -0800247 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700248
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000249
Dan Shi7279a5a2016-04-07 11:04:28 -0700250 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700251 def _get_action_priority(cls, name):
252 """Return priority for the action with the given name."""
253 if name in cls._priorities:
254 return cls._priorities.index(name)
255 else:
256 return sys.maxint
Dan Shi7279a5a2016-04-07 11:04:28 -0700257
258
Alex Miller1968edf2014-02-27 18:11:36 -0800259class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700260 """
Alex Miller1968edf2014-02-27 18:11:36 -0800261 Tests to verify that the DUT is in a sane, known good state that we can run
262 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700263 """
Alex Miller1968edf2014-02-27 18:11:36 -0800264
265 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800266 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000267 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
268 # is stable in lab (destiny). The power_RPMTest failure led to reset job
269 # failure and that left dut in Repair Failed. Since the test will fail
270 # anyway due to the destiny lab issue, and test retry will retry the
271 # test in another DUT.
272 # This change temporarily disable the RPM check in reset job.
273 # Another way to do this is to remove rpm dependency from tests' control
274 # file. That will involve changes on multiple control files. This one
275 # line change here is a simple temporary fix.
276 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800277 }
278
279 name = 'verify'
280
281
282class Provision(_SpecialTaskAction):
283 """
284 Provisioning runs to change the configuration of the DUT from one state to
285 another. It will only be run on verified DUTs.
286 """
287
Dan Shi7279a5a2016-04-07 11:04:28 -0700288 # ChromeOS update must happen before firmware install, so the dut has the
289 # correct ChromeOS version label when firmware install occurs. The ChromeOS
290 # version label is used for firmware update to stage desired ChromeOS image
291 # on to the servo USB stick.
292 _priorities = [CROS_VERSION_PREFIX,
293 FW_RO_VERSION_PREFIX,
294 FW_RW_VERSION_PREFIX]
295
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}),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800307 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800308 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800309 FW_RW_VERSION_PREFIX: actionables.TestActionable(
310 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700311 extra_kwargs={'rw_only': True,
312 'tag': 'rw_only'}),
Simran Basi5ace6f22016-01-06 17:30:44 -0800313 ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
314 'provision_AndroidUpdate'),
Simran Basiadf31312016-06-28 14:23:05 -0700315 TESTBED_BUILD_VERSION_PREFIX : actionables.TestActionable(
316 'provision_TestbedUpdate'),
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
335class Repair(_SpecialTaskAction):
336 """
337 Repair runs when one of the other special tasks fails. It should be able
338 to take a component of the DUT that's in an unknown state and restore it to
339 a good state.
340 """
341
342 _actions = {
343 }
344
345 name = 'repair'
346
347
Alex Milleraa772002014-04-10 17:51:21 -0700348# TODO(milleral): crbug.com/364273
349# Label doesn't really mean label in this context. We're putting things into
350# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
351# doing that.
352def is_for_special_action(label):
353 """
354 If any special task handles the label specially, then we're using the label
355 to communicate that we want an action, and not as an actual dependency that
356 the test has.
357
358 @param label: A string label name.
359 @return True if any special task handles this label specially,
360 False if no special task handles this label.
361 """
362 return (Verify.acts_on(label) or
363 Provision.acts_on(label) or
364 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800365 Repair.acts_on(label) or
366 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700367
368
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000369def join(provision_type, provision_value):
370 """
371 Combine the provision type and value into the label name.
372
373 @param provision_type: One of the constants that are the label prefixes.
374 @param provision_value: A string of the value for this provision type.
375 @returns: A string that is the label name for this (type, value) pair.
376
377 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
378 'cros-version:lumpy-release/R27-3773.0.0'
379
380 """
381 return '%s:%s' % (provision_type, provision_value)
382
383
Alex Miller667b5f22014-02-28 15:33:39 -0800384class SpecialTaskActionException(Exception):
385 """
386 Exception raised when a special task fails to successfully run a test that
387 is required.
388
389 This is also a literally meaningless exception. It's always just discarded.
390 """
391
392
393def run_special_task_actions(job, host, labels, task):
394 """
395 Iterate through all `label`s and run any tests on `host` that `task` has
396 corresponding to the passed in labels.
397
398 Emits status lines for each run test, and INFO lines for each skipped label.
399
400 @param job: A job object from a control file.
401 @param host: The host to run actions on.
402 @param labels: The list of job labels to work on.
403 @param task: An instance of _SpecialTaskAction.
404 @returns: None
405 @raises: SpecialTaskActionException if a test fails.
406
407 """
Allen Lid575f642017-05-23 13:07:28 -0700408 warnings.warn('run_special_task_actions is deprecated', stacklevel=2)
Allen Li21154582017-02-22 18:00:42 -0800409 task.run_task_actions(job, host, labels)