blob: ee389ede6c796d6adf90726fff324ddccf727f05 [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 Makasana37f5cf02017-06-08 17:21:25 -070018CROS_TH_VERSION_PREFIX = Key.CROS_TH_VERSION
Allen Li89711f72017-02-21 18:21:52 -080019ANDROID_BUILD_VERSION_PREFIX = Key.ANDROID_BUILD_VERSION
20TESTBED_BUILD_VERSION_PREFIX = Key.TESTBED_VERSION
21FW_RW_VERSION_PREFIX = Key.FIRMWARE_RW_VERSION
22FW_RO_VERSION_PREFIX = Key.FIRMWARE_RO_VERSION
Alex Miller0516e4c2013-06-03 18:07:48 -070023
Richard Barnette6c2b70a2017-01-26 13:40:51 -080024_ANDROID_BUILD_REGEX = r'.+/.+/P?([0-9]+|LATEST)'
25_ANDROID_TESTBED_BUILD_REGEX = _ANDROID_BUILD_REGEX + '(,|(#[0-9]+))'
Rohit Makasana37f5cf02017-06-08 17:21:25 -070026_CROS_TH_BUILD_REGEX = r'.+-release/.+;.+/.+/P?[0-9]+$'
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
Chris Sosae92399e2015-04-24 11:32:59 -070031# Default number of provisions attempts to try if we believe the devserver is
32# flaky.
33FLAKY_DEVSERVER_ATTEMPTS = 2
34
Alex Miller0516e4c2013-06-03 18:07:48 -070035
Allen Li606673f2017-02-21 18:41:13 -080036_Action = collections.namedtuple('_Action', 'name, value')
37
38
39def _get_label_action(str_label):
40 """Get action represented by the label.
41
42 This is used for determine actions to perform based on labels, for
43 example for provisioning or repair.
44
45 @param str_label: label string
46 @returns: _Action instance
47 """
48 try:
49 keyval_label = labellib.parse_keyval_label(str_label)
50 except ValueError:
51 return _Action(str_label, None)
52 else:
53 return _Action(keyval_label.key, keyval_label.value)
54
55
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000056### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080057def get_version_label_prefix(image):
58 """
59 Determine a version label prefix from a given image name.
60
61 Parses `image` to determine what kind of image it refers
62 to, and returns the corresponding version label prefix.
63
64 Known version label prefixes are:
65 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
66 These images have names like `cave-release/R57-9030.0.0`.
Rohit Makasana37f5cf02017-06-08 17:21:25 -070067 * `CROS_TH_VERSION_PREFIX` for Chrome OS ARC TH version strings.
68 These images have names like
69 `cyan-release/R60-9517.0.0;git_nyc-arc/cheets_x86-user/3512523`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080070 * `ANDROID_BUILD_VERSION_PREFIX` for Android build versions
71 These images have names like
72 `git_mnc-release/shamu-userdebug/2457013`.
73 * `TESTBED_BUILD_VERSION_PREFIX` for Android testbed version
74 specifications. These are either comma separated lists of
75 Android versions, or an Android version with a suffix like
76 '#2', indicating two devices running the given build.
77
78 @param image: The image name to be parsed.
79 @returns: A string that is the prefix of version labels for the type
80 of image identified by `image`.
81
82 """
83 if re.match(_ANDROID_TESTBED_BUILD_REGEX, image, re.I):
84 return TESTBED_BUILD_VERSION_PREFIX
85 elif re.match(_ANDROID_BUILD_REGEX, image, re.I):
86 return ANDROID_BUILD_VERSION_PREFIX
Rohit Makasana37f5cf02017-06-08 17:21:25 -070087 elif re.match(_CROS_TH_BUILD_REGEX, image, re.I):
88 return CROS_TH_VERSION_PREFIX
Richard Barnette6c2b70a2017-01-26 13:40:51 -080089 else:
90 return CROS_VERSION_PREFIX
91
92
93def image_version_to_label(image):
94 """
95 Return a version label appropriate to the given image name.
96
97 The type of version label is as determined described for
98 `get_version_label_prefix()`, meaning the label will identify a
99 CrOS, Android, or Testbed version.
100
101 @param image: The image name to be parsed.
102 @returns: A string that is the appropriate label name.
103
104 """
105 return get_version_label_prefix(image) + ':' + image
106
107
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000108def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -0700109 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000110 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800111
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000112 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
113 @returns: A string that is the appropriate label name.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800114
115 """
Allen Liaaabda82017-02-21 18:27:38 -0800116 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
117 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
118 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800119
120
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000121def fwrw_version_to_label(image):
122 """
123 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700124
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000125 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
126 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700127
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000128 """
Allen Liaaabda82017-02-21 18:27:38 -0800129 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
130 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
131 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700132
133
Alex Miller1968edf2014-02-27 18:11:36 -0800134class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700135 """
Alex Miller1968edf2014-02-27 18:11:36 -0800136 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700137 """
Alex Miller1968edf2014-02-27 18:11:36 -0800138
Dan Shi7279a5a2016-04-07 11:04:28 -0700139 # A dictionary mapping labels to test names.
140 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700141
Dan Shi7279a5a2016-04-07 11:04:28 -0700142 # The name of this special task to be used in output.
143 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700144
Dan Shi7279a5a2016-04-07 11:04:28 -0700145 # Some special tasks require to run before others, e.g., ChromeOS image
146 # needs to be updated before firmware provision. List `_priorities` defines
147 # the order of each label prefix. An element with a smaller index has higher
148 # priority. Not listed ones have the lowest priority.
149 # This property should be overriden in subclass to define its own priorities
150 # across available label prefixes.
151 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800152
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000153
Alex Miller1968edf2014-02-27 18:11:36 -0800154 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000155 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800156 """
157 Returns True if the label is a label that we recognize as something we
158 know how to act on, given our _actions.
159
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000160 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800161 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800162 """
Allen Li95e930a2017-02-21 18:31:18 -0800163 action = _get_label_action(label)
164 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000165
Alex Miller1968edf2014-02-27 18:11:36 -0800166
167 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800168 def run_task_actions(cls, job, host, labels):
169 """
170 Run task actions on host that correspond to the labels.
171
172 Emits status lines for each run test, and INFO lines for each
173 skipped label.
174
175 @param job: A job object from a control file.
176 @param host: The host to run actions on.
177 @param labels: The list of job labels to work on.
178 @raises: SpecialTaskActionException if a test fails.
179 """
Allen Lid575f642017-05-23 13:07:28 -0700180 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800181 for label in unactionable:
182 job.record('INFO', None, cls.name,
183 "Can't %s label '%s'." % (cls.name, label))
184
Allen Lid575f642017-05-23 13:07:28 -0700185 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800186 success = action_item.execute(job=job, host=host, value=value)
187 if not success:
188 raise SpecialTaskActionException()
189
190
191 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700192 def _actions_and_values_iter(cls, labels):
193 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800194
Allen Lid575f642017-05-23 13:07:28 -0700195 @params: An iterable of label strings.
196 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800197 """
Allen Lid575f642017-05-23 13:07:28 -0700198 actionable = cls._filter_actionable_labels(labels)
199 keyval_mapping = labellib.LabelsMapping(actionable)
200 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
201 for name in sorted_names:
202 action_item = cls._actions[name]
203 value = keyval_mapping[name]
204 yield action_item, value
205
206
207 @classmethod
208 def _filter_unactionable_labels(cls, labels):
209 """
210 Return labels that we cannot act on.
211
212 @param labels: A list of strings of labels.
213 @returns: A set of unactionable labels
214 """
215 return {label for label in labels
216 if not (label == SKIP_PROVISION or cls.acts_on(label))}
217
218
219 @classmethod
220 def _filter_actionable_labels(cls, labels):
221 """
222 Return labels that we can act on.
223
224 @param labels: A list of strings of labels.
225 @returns: A set of actionable labels
226 """
227 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800228
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000229
Alex Milleraa772002014-04-10 17:51:21 -0700230 @classmethod
231 def partition(cls, labels):
232 """
233 Filter a list of labels into two sets: those labels that we know how to
234 act on and those that we don't know how to act on.
235
236 @param labels: A list of strings of labels.
237 @returns: A tuple where the first element is a set of unactionable
238 labels, and the second element is a set of the actionable
239 labels.
240 """
Allen Li5b623122017-02-22 17:42:02 -0800241 unactionable = set()
242 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700243
244 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800245 if label == SKIP_PROVISION:
246 # skip_provision is neither actionable or a capability label.
247 # It doesn't need any handling.
248 continue
249 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800250 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700251 else:
Allen Li5b623122017-02-22 17:42:02 -0800252 unactionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700253
Allen Li5b623122017-02-22 17:42:02 -0800254 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700255
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000256
Dan Shi7279a5a2016-04-07 11:04:28 -0700257 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700258 def _get_action_priority(cls, name):
259 """Return priority for the action with the given name."""
260 if name in cls._priorities:
261 return cls._priorities.index(name)
262 else:
263 return sys.maxint
Dan Shi7279a5a2016-04-07 11:04:28 -0700264
265
Alex Miller1968edf2014-02-27 18:11:36 -0800266class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700267 """
Alex Miller1968edf2014-02-27 18:11:36 -0800268 Tests to verify that the DUT is in a sane, known good state that we can run
269 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700270 """
Alex Miller1968edf2014-02-27 18:11:36 -0800271
272 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800273 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000274 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
275 # is stable in lab (destiny). The power_RPMTest failure led to reset job
276 # failure and that left dut in Repair Failed. Since the test will fail
277 # anyway due to the destiny lab issue, and test retry will retry the
278 # test in another DUT.
279 # This change temporarily disable the RPM check in reset job.
280 # Another way to do this is to remove rpm dependency from tests' control
281 # file. That will involve changes on multiple control files. This one
282 # line change here is a simple temporary fix.
283 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800284 }
285
286 name = 'verify'
287
288
289class Provision(_SpecialTaskAction):
290 """
291 Provisioning runs to change the configuration of the DUT from one state to
292 another. It will only be run on verified DUTs.
293 """
294
Dan Shi7279a5a2016-04-07 11:04:28 -0700295 # ChromeOS update must happen before firmware install, so the dut has the
296 # correct ChromeOS version label when firmware install occurs. The ChromeOS
297 # version label is used for firmware update to stage desired ChromeOS image
298 # on to the servo USB stick.
299 _priorities = [CROS_VERSION_PREFIX,
300 FW_RO_VERSION_PREFIX,
301 FW_RW_VERSION_PREFIX]
302
Alex Miller1968edf2014-02-27 18:11:36 -0800303 # TODO(milleral): http://crbug.com/249555
304 # Create some way to discover and register provisioning tests so that we
305 # don't need to hand-maintain a list of all of them.
306 _actions = {
Fang Deng9d548742015-02-03 11:35:02 -0800307 CROS_VERSION_PREFIX: actionables.TestActionable(
308 'provision_AutoUpdate',
309 extra_kwargs={'disable_sysinfo': False,
310 'disable_before_test_sysinfo': False,
311 'disable_before_iteration_sysinfo': True,
312 'disable_after_test_sysinfo': True,
313 'disable_after_iteration_sysinfo': True}),
Rohit Makasana37f5cf02017-06-08 17:21:25 -0700314 CROS_TH_VERSION_PREFIX : actionables.TestActionable(
315 'provision_CheetsUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800316 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800317 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800318 FW_RW_VERSION_PREFIX: actionables.TestActionable(
319 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700320 extra_kwargs={'rw_only': True,
321 'tag': 'rw_only'}),
Simran Basi5ace6f22016-01-06 17:30:44 -0800322 ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
323 'provision_AndroidUpdate'),
Simran Basiadf31312016-06-28 14:23:05 -0700324 TESTBED_BUILD_VERSION_PREFIX : actionables.TestActionable(
325 'provision_TestbedUpdate'),
Alex Miller1968edf2014-02-27 18:11:36 -0800326 }
327
328 name = 'provision'
329
330
331class Cleanup(_SpecialTaskAction):
332 """
333 Cleanup runs after a test fails to try and remove artifacts of tests and
334 ensure the DUT will be in a sane state for the next test run.
335 """
336
337 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800338 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800339 }
340
341 name = 'cleanup'
342
343
344class 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 """
400
401
402def run_special_task_actions(job, host, labels, task):
403 """
404 Iterate through all `label`s and run any tests on `host` that `task` has
405 corresponding to the passed in labels.
406
407 Emits status lines for each run test, and INFO lines for each skipped label.
408
409 @param job: A job object from a control file.
410 @param host: The host to run actions on.
411 @param labels: The list of job labels to work on.
412 @param task: An instance of _SpecialTaskAction.
413 @returns: None
414 @raises: SpecialTaskActionException if a test fails.
415
416 """
Allen Lid575f642017-05-23 13:07:28 -0700417 warnings.warn('run_special_task_actions is deprecated', stacklevel=2)
Allen Li21154582017-02-22 18:00:42 -0800418 task.run_task_actions(job, host, labels)