blob: dc0c0bfad4814113615fd6a8f9ed06a1feff21d5 [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 -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
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070024# So far the word cheets is only way to distinguish between ARC and Android
25# build.
26_ANDROID_BUILD_REGEX = r'.+/(?!cheets).+/P?([0-9]+|LATEST)'
Richard Barnette6c2b70a2017-01-26 13:40:51 -080027_ANDROID_TESTBED_BUILD_REGEX = _ANDROID_BUILD_REGEX + '(,|(#[0-9]+))'
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070028_CROS_ANDROID_BUILD_REGEX = r'.+/(?=cheets).+/P?([0-9]+|LATEST)'
Richard Barnette6c2b70a2017-01-26 13:40:51 -080029
Dan Shie44f9c02016-02-18 13:25:05 -080030# Special label to skip provision and run reset instead.
31SKIP_PROVISION = 'skip_provision'
32
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070033# Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning.
34CHEETS_SUFFIX = '-cheetsth'
35
Chris Sosae92399e2015-04-24 11:32:59 -070036# Default number of provisions attempts to try if we believe the devserver is
37# flaky.
38FLAKY_DEVSERVER_ATTEMPTS = 2
39
Alex Miller0516e4c2013-06-03 18:07:48 -070040
Allen Li606673f2017-02-21 18:41:13 -080041_Action = collections.namedtuple('_Action', 'name, value')
42
43
44def _get_label_action(str_label):
45 """Get action represented by the label.
46
47 This is used for determine actions to perform based on labels, for
48 example for provisioning or repair.
49
50 @param str_label: label string
51 @returns: _Action instance
52 """
53 try:
54 keyval_label = labellib.parse_keyval_label(str_label)
55 except ValueError:
56 return _Action(str_label, None)
57 else:
58 return _Action(keyval_label.key, keyval_label.value)
59
60
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000061### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080062def get_version_label_prefix(image):
63 """
64 Determine a version label prefix from a given image name.
65
66 Parses `image` to determine what kind of image it refers
67 to, and returns the corresponding version label prefix.
68
69 Known version label prefixes are:
70 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
71 These images have names like `cave-release/R57-9030.0.0`.
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070072 * `CROS_ANDROID_VERSION_PREFIX` for Chrome OS Android version strings.
73 These images have names like `git_nyc-arc/cheets_x86-user/3512523`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080074 * `ANDROID_BUILD_VERSION_PREFIX` for Android build versions
75 These images have names like
76 `git_mnc-release/shamu-userdebug/2457013`.
77 * `TESTBED_BUILD_VERSION_PREFIX` for Android testbed version
78 specifications. These are either comma separated lists of
79 Android versions, or an Android version with a suffix like
80 '#2', indicating two devices running the given build.
81
82 @param image: The image name to be parsed.
83 @returns: A string that is the prefix of version labels for the type
84 of image identified by `image`.
85
86 """
87 if re.match(_ANDROID_TESTBED_BUILD_REGEX, image, re.I):
88 return TESTBED_BUILD_VERSION_PREFIX
89 elif re.match(_ANDROID_BUILD_REGEX, image, re.I):
90 return ANDROID_BUILD_VERSION_PREFIX
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070091 elif re.match(_CROS_ANDROID_BUILD_REGEX, image, re.I):
92 return CROS_ANDROID_VERSION_PREFIX
Richard Barnette6c2b70a2017-01-26 13:40:51 -080093 else:
94 return CROS_VERSION_PREFIX
95
96
97def image_version_to_label(image):
98 """
99 Return a version label appropriate to the given image name.
100
101 The type of version label is as determined described for
102 `get_version_label_prefix()`, meaning the label will identify a
103 CrOS, Android, or Testbed version.
104
105 @param image: The image name to be parsed.
106 @returns: A string that is the appropriate label name.
107
108 """
109 return get_version_label_prefix(image) + ':' + image
110
111
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000112def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -0700113 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000114 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800115
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.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800118
119 """
Allen Liaaabda82017-02-21 18:27:38 -0800120 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
121 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
122 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800123
124
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000125def fwrw_version_to_label(image):
126 """
127 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700128
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000129 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
130 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700131
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000132 """
Allen Liaaabda82017-02-21 18:27:38 -0800133 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
134 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
135 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700136
137
Alex Miller1968edf2014-02-27 18:11:36 -0800138class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700139 """
Alex Miller1968edf2014-02-27 18:11:36 -0800140 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700141 """
Alex Miller1968edf2014-02-27 18:11:36 -0800142
Dan Shi7279a5a2016-04-07 11:04:28 -0700143 # A dictionary mapping labels to test names.
144 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700145
Dan Shi7279a5a2016-04-07 11:04:28 -0700146 # The name of this special task to be used in output.
147 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700148
Dan Shi7279a5a2016-04-07 11:04:28 -0700149 # Some special tasks require to run before others, e.g., ChromeOS image
150 # needs to be updated before firmware provision. List `_priorities` defines
151 # the order of each label prefix. An element with a smaller index has higher
152 # priority. Not listed ones have the lowest priority.
153 # This property should be overriden in subclass to define its own priorities
154 # across available label prefixes.
155 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800156
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000157
Alex Miller1968edf2014-02-27 18:11:36 -0800158 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000159 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800160 """
161 Returns True if the label is a label that we recognize as something we
162 know how to act on, given our _actions.
163
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000164 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800165 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800166 """
Allen Li95e930a2017-02-21 18:31:18 -0800167 action = _get_label_action(label)
168 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000169
Alex Miller1968edf2014-02-27 18:11:36 -0800170
171 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800172 def run_task_actions(cls, job, host, labels):
173 """
174 Run task actions on host that correspond to the labels.
175
176 Emits status lines for each run test, and INFO lines for each
177 skipped label.
178
179 @param job: A job object from a control file.
180 @param host: The host to run actions on.
181 @param labels: The list of job labels to work on.
182 @raises: SpecialTaskActionException if a test fails.
183 """
Allen Lid575f642017-05-23 13:07:28 -0700184 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800185 for label in unactionable:
186 job.record('INFO', None, cls.name,
187 "Can't %s label '%s'." % (cls.name, label))
188
Allen Lid575f642017-05-23 13:07:28 -0700189 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800190 success = action_item.execute(job=job, host=host, value=value)
191 if not success:
192 raise SpecialTaskActionException()
193
194
195 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700196 def _actions_and_values_iter(cls, labels):
197 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800198
Allen Lid575f642017-05-23 13:07:28 -0700199 @params: An iterable of label strings.
200 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800201 """
Allen Lid575f642017-05-23 13:07:28 -0700202 actionable = cls._filter_actionable_labels(labels)
203 keyval_mapping = labellib.LabelsMapping(actionable)
204 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
205 for name in sorted_names:
206 action_item = cls._actions[name]
207 value = keyval_mapping[name]
208 yield action_item, value
209
210
211 @classmethod
212 def _filter_unactionable_labels(cls, labels):
213 """
214 Return labels that we cannot act on.
215
216 @param labels: A list of strings of labels.
217 @returns: A set of unactionable labels
218 """
219 return {label for label in labels
220 if not (label == SKIP_PROVISION or cls.acts_on(label))}
221
222
223 @classmethod
224 def _filter_actionable_labels(cls, labels):
225 """
226 Return labels that we can act on.
227
228 @param labels: A list of strings of labels.
229 @returns: A set of actionable labels
230 """
231 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800232
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000233
Alex Milleraa772002014-04-10 17:51:21 -0700234 @classmethod
235 def partition(cls, labels):
236 """
237 Filter a list of labels into two sets: those labels that we know how to
238 act on and those that we don't know how to act on.
239
240 @param labels: A list of strings of labels.
241 @returns: A tuple where the first element is a set of unactionable
242 labels, and the second element is a set of the actionable
243 labels.
244 """
Allen Li5b623122017-02-22 17:42:02 -0800245 unactionable = set()
246 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700247
248 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800249 if label == SKIP_PROVISION:
250 # skip_provision is neither actionable or a capability label.
251 # It doesn't need any handling.
252 continue
253 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800254 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700255 else:
Allen Li5b623122017-02-22 17:42:02 -0800256 unactionable.add(label)
Rohit Makasanac4eacc92017-09-06 13:49:38 -0700257
Allen Li5b623122017-02-22 17:42:02 -0800258 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700259
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000260
Dan Shi7279a5a2016-04-07 11:04:28 -0700261 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700262 def _get_action_priority(cls, name):
263 """Return priority for the action with the given name."""
264 if name in cls._priorities:
265 return cls._priorities.index(name)
266 else:
267 return sys.maxint
Dan Shi7279a5a2016-04-07 11:04:28 -0700268
269
Alex Miller1968edf2014-02-27 18:11:36 -0800270class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700271 """
Alex Miller1968edf2014-02-27 18:11:36 -0800272 Tests to verify that the DUT is in a sane, known good state that we can run
273 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700274 """
Alex Miller1968edf2014-02-27 18:11:36 -0800275
276 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800277 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000278 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
279 # is stable in lab (destiny). The power_RPMTest failure led to reset job
280 # failure and that left dut in Repair Failed. Since the test will fail
281 # anyway due to the destiny lab issue, and test retry will retry the
282 # test in another DUT.
283 # This change temporarily disable the RPM check in reset job.
284 # Another way to do this is to remove rpm dependency from tests' control
285 # file. That will involve changes on multiple control files. This one
286 # line change here is a simple temporary fix.
287 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800288 }
289
290 name = 'verify'
291
292
293class Provision(_SpecialTaskAction):
294 """
295 Provisioning runs to change the configuration of the DUT from one state to
296 another. It will only be run on verified DUTs.
297 """
298
Dan Shi7279a5a2016-04-07 11:04:28 -0700299 # ChromeOS update must happen before firmware install, so the dut has the
300 # correct ChromeOS version label when firmware install occurs. The ChromeOS
301 # version label is used for firmware update to stage desired ChromeOS image
302 # on to the servo USB stick.
303 _priorities = [CROS_VERSION_PREFIX,
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700304 CROS_ANDROID_VERSION_PREFIX,
Dan Shi7279a5a2016-04-07 11:04:28 -0700305 FW_RO_VERSION_PREFIX,
306 FW_RW_VERSION_PREFIX]
307
Alex Miller1968edf2014-02-27 18:11:36 -0800308 # TODO(milleral): http://crbug.com/249555
309 # Create some way to discover and register provisioning tests so that we
310 # don't need to hand-maintain a list of all of them.
311 _actions = {
Fang Deng9d548742015-02-03 11:35:02 -0800312 CROS_VERSION_PREFIX: actionables.TestActionable(
313 'provision_AutoUpdate',
314 extra_kwargs={'disable_sysinfo': False,
315 'disable_before_test_sysinfo': False,
316 'disable_before_iteration_sysinfo': True,
317 'disable_after_test_sysinfo': True,
318 'disable_after_iteration_sysinfo': True}),
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700319 CROS_ANDROID_VERSION_PREFIX : actionables.TestActionable(
Rohit Makasana37f5cf02017-06-08 17:21:25 -0700320 'provision_CheetsUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800321 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800322 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800323 FW_RW_VERSION_PREFIX: actionables.TestActionable(
324 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700325 extra_kwargs={'rw_only': True,
326 'tag': 'rw_only'}),
Simran Basi5ace6f22016-01-06 17:30:44 -0800327 ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
328 'provision_AndroidUpdate'),
Simran Basiadf31312016-06-28 14:23:05 -0700329 TESTBED_BUILD_VERSION_PREFIX : actionables.TestActionable(
330 'provision_TestbedUpdate'),
Alex Miller1968edf2014-02-27 18:11:36 -0800331 }
332
333 name = 'provision'
334
335
336class Cleanup(_SpecialTaskAction):
337 """
338 Cleanup runs after a test fails to try and remove artifacts of tests and
339 ensure the DUT will be in a sane state for the next test run.
340 """
341
342 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800343 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800344 }
345
346 name = 'cleanup'
347
348
349class Repair(_SpecialTaskAction):
350 """
351 Repair runs when one of the other special tasks fails. It should be able
352 to take a component of the DUT that's in an unknown state and restore it to
353 a good state.
354 """
355
356 _actions = {
357 }
358
359 name = 'repair'
360
361
Alex Milleraa772002014-04-10 17:51:21 -0700362# TODO(milleral): crbug.com/364273
363# Label doesn't really mean label in this context. We're putting things into
364# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
365# doing that.
366def is_for_special_action(label):
367 """
368 If any special task handles the label specially, then we're using the label
369 to communicate that we want an action, and not as an actual dependency that
370 the test has.
371
372 @param label: A string label name.
373 @return True if any special task handles this label specially,
374 False if no special task handles this label.
375 """
376 return (Verify.acts_on(label) or
377 Provision.acts_on(label) or
378 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800379 Repair.acts_on(label) or
380 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700381
382
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000383def join(provision_type, provision_value):
384 """
385 Combine the provision type and value into the label name.
386
387 @param provision_type: One of the constants that are the label prefixes.
388 @param provision_value: A string of the value for this provision type.
389 @returns: A string that is the label name for this (type, value) pair.
390
391 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
392 'cros-version:lumpy-release/R27-3773.0.0'
393
394 """
395 return '%s:%s' % (provision_type, provision_value)
396
397
Alex Miller667b5f22014-02-28 15:33:39 -0800398class SpecialTaskActionException(Exception):
399 """
400 Exception raised when a special task fails to successfully run a test that
401 is required.
402
403 This is also a literally meaningless exception. It's always just discarded.
404 """
405
406
407def run_special_task_actions(job, host, labels, task):
408 """
409 Iterate through all `label`s and run any tests on `host` that `task` has
410 corresponding to the passed in labels.
411
412 Emits status lines for each run test, and INFO lines for each skipped label.
413
414 @param job: A job object from a control file.
415 @param host: The host to run actions on.
416 @param labels: The list of job labels to work on.
417 @param task: An instance of _SpecialTaskAction.
418 @returns: None
419 @raises: SpecialTaskActionException if a test fails.
420
421 """
Allen Lid575f642017-05-23 13:07:28 -0700422 warnings.warn('run_special_task_actions is deprecated', stacklevel=2)
Allen Li21154582017-02-22 18:00:42 -0800423 task.run_task_actions(job, host, labels)