blob: 2a09b175b00e22720ea8a4a2f290f996988f088a [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
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +00005
Richard Barnette6c2b70a2017-01-26 13:40:51 -08006import re
Dan Shi7279a5a2016-04-07 11:04:28 -07007import sys
Alex Miller0516e4c2013-06-03 18:07:48 -07008
9import common
Fang Dengaf30e7c2014-11-15 13:57:03 -080010from autotest_lib.server.cros import provision_actionables as actionables
Alex Miller0516e4c2013-06-03 18:07:48 -070011
12
13### Constants for label prefixes
14CROS_VERSION_PREFIX = 'cros-version'
Simran Basi5ace6f22016-01-06 17:30:44 -080015ANDROID_BUILD_VERSION_PREFIX = 'ab-version'
Kevin Cheng84a71ba2016-07-14 11:03:57 -070016TESTBED_BUILD_VERSION_PREFIX = 'testbed-version'
Dan Shi0723bf52015-06-24 10:52:38 -070017FW_RW_VERSION_PREFIX = 'fwrw-version'
Dan Shi36cfd832014-10-10 13:38:51 -070018FW_RO_VERSION_PREFIX = 'fwro-version'
Alex Miller0516e4c2013-06-03 18:07:48 -070019
Richard Barnette6c2b70a2017-01-26 13:40:51 -080020_ANDROID_BUILD_REGEX = r'.+/.+/P?([0-9]+|LATEST)'
21_ANDROID_TESTBED_BUILD_REGEX = _ANDROID_BUILD_REGEX + '(,|(#[0-9]+))'
22
Dan Shie44f9c02016-02-18 13:25:05 -080023# Special label to skip provision and run reset instead.
24SKIP_PROVISION = 'skip_provision'
25
Chris Sosae92399e2015-04-24 11:32:59 -070026# Default number of provisions attempts to try if we believe the devserver is
27# flaky.
28FLAKY_DEVSERVER_ATTEMPTS = 2
29
Alex Miller0516e4c2013-06-03 18:07:48 -070030
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000031### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080032def get_version_label_prefix(image):
33 """
34 Determine a version label prefix from a given image name.
35
36 Parses `image` to determine what kind of image it refers
37 to, and returns the corresponding version label prefix.
38
39 Known version label prefixes are:
40 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
41 These images have names like `cave-release/R57-9030.0.0`.
42 * `ANDROID_BUILD_VERSION_PREFIX` for Android build versions
43 These images have names like
44 `git_mnc-release/shamu-userdebug/2457013`.
45 * `TESTBED_BUILD_VERSION_PREFIX` for Android testbed version
46 specifications. These are either comma separated lists of
47 Android versions, or an Android version with a suffix like
48 '#2', indicating two devices running the given build.
49
50 @param image: The image name to be parsed.
51 @returns: A string that is the prefix of version labels for the type
52 of image identified by `image`.
53
54 """
55 if re.match(_ANDROID_TESTBED_BUILD_REGEX, image, re.I):
56 return TESTBED_BUILD_VERSION_PREFIX
57 elif re.match(_ANDROID_BUILD_REGEX, image, re.I):
58 return ANDROID_BUILD_VERSION_PREFIX
59 else:
60 return CROS_VERSION_PREFIX
61
62
63def image_version_to_label(image):
64 """
65 Return a version label appropriate to the given image name.
66
67 The type of version label is as determined described for
68 `get_version_label_prefix()`, meaning the label will identify a
69 CrOS, Android, or Testbed version.
70
71 @param image: The image name to be parsed.
72 @returns: A string that is the appropriate label name.
73
74 """
75 return get_version_label_prefix(image) + ':' + image
76
77
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000078def cros_version_to_label(image):
Alex Miller0516e4c2013-06-03 18:07:48 -070079 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000080 Returns the proper label name for a ChromeOS build of |image|.
Alex Miller0516e4c2013-06-03 18:07:48 -070081
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000082 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
83 @returns: A string that is the appropriate label name.
Alex Miller0516e4c2013-06-03 18:07:48 -070084
85 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000086 return CROS_VERSION_PREFIX + ':' + image
Alex Miller0516e4c2013-06-03 18:07:48 -070087
88
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000089def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -070090 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000091 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080092
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000093 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
94 @returns: A string that is the appropriate label name.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080095
96 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000097 return FW_RO_VERSION_PREFIX + ':' + image
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080098
99
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000100def fwrw_version_to_label(image):
101 """
102 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700103
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000104 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
105 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700106
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000107 """
108 return FW_RW_VERSION_PREFIX + ':' + image
Dan Shi9cb0eec2014-06-03 09:04:50 -0700109
110
Alex Miller1968edf2014-02-27 18:11:36 -0800111class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700112 """
Alex Miller1968edf2014-02-27 18:11:36 -0800113 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700114 """
Alex Miller1968edf2014-02-27 18:11:36 -0800115
Dan Shi7279a5a2016-04-07 11:04:28 -0700116 # A dictionary mapping labels to test names.
117 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700118
Dan Shi7279a5a2016-04-07 11:04:28 -0700119 # The name of this special task to be used in output.
120 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700121
Dan Shi7279a5a2016-04-07 11:04:28 -0700122 # Some special tasks require to run before others, e.g., ChromeOS image
123 # needs to be updated before firmware provision. List `_priorities` defines
124 # the order of each label prefix. An element with a smaller index has higher
125 # priority. Not listed ones have the lowest priority.
126 # This property should be overriden in subclass to define its own priorities
127 # across available label prefixes.
128 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800129
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000130
Alex Miller1968edf2014-02-27 18:11:36 -0800131 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000132 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800133 """
134 Returns True if the label is a label that we recognize as something we
135 know how to act on, given our _actions.
136
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000137 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800138 @returns: True if there exists a test to run for this label.
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000139
Alex Miller1968edf2014-02-27 18:11:36 -0800140 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000141 return label.split(':')[0] in cls._actions
142
Alex Miller1968edf2014-02-27 18:11:36 -0800143
144 @classmethod
145 def test_for(cls, label):
146 """
147 Returns the test associated with the given (string) label name.
148
149 @param label: The label for which the action is being requested.
150 @returns: The string name of the test that should be run.
151 @raises KeyError: If the name was not recognized as one we care about.
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000152
Alex Miller1968edf2014-02-27 18:11:36 -0800153 """
154 return cls._actions[label]
155
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000156
Alex Milleraa772002014-04-10 17:51:21 -0700157 @classmethod
158 def partition(cls, labels):
159 """
160 Filter a list of labels into two sets: those labels that we know how to
161 act on and those that we don't know how to act on.
162
163 @param labels: A list of strings of labels.
164 @returns: A tuple where the first element is a set of unactionable
165 labels, and the second element is a set of the actionable
166 labels.
167 """
168 capabilities = set()
169 configurations = set()
170
171 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800172 if label == SKIP_PROVISION:
173 # skip_provision is neither actionable or a capability label.
174 # It doesn't need any handling.
175 continue
176 elif cls.acts_on(label):
Alex Milleraa772002014-04-10 17:51:21 -0700177 configurations.add(label)
178 else:
179 capabilities.add(label)
180
181 return capabilities, configurations
182
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000183
Dan Shi7279a5a2016-04-07 11:04:28 -0700184 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000185 def sort_configurations(cls, configurations):
Dan Shi7279a5a2016-04-07 11:04:28 -0700186 """
187 Sort configurations based on the priority defined in cls._priorities.
188
189 @param configurations: A list of actionable labels.
Dan Shi7279a5a2016-04-07 11:04:28 -0700190
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000191 @return: A sorted list of tuple of (label_prefix, value), the tuples are
192 sorted based on the label_prefix's index in cls._priorities.
Allen Lifda3e232016-10-17 14:54:12 -0700193 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000194 # Split a list of labels into a dict mapping name to value. All labels
195 # must be provisionable labels, or else a ValueError
196 # For example, label 'cros-version:lumpy-release/R28-3993.0.0' is split
197 # to {'cros-version': 'lumpy-release/R28-3993.0.0'}
198 split_configurations = dict()
199 for label in configurations:
200 name, _, value = label.partition(':')
201 split_configurations[name] = value
Allen Lifda3e232016-10-17 14:54:12 -0700202
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000203 sort_key = (lambda config:
204 (cls._priorities.index(config[0])
205 if (config[0] in cls._priorities) else sys.maxint))
206 return sorted(split_configurations.items(), key=sort_key)
Dan Shi7279a5a2016-04-07 11:04:28 -0700207
208
Alex Miller1968edf2014-02-27 18:11:36 -0800209class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700210 """
Alex Miller1968edf2014-02-27 18:11:36 -0800211 Tests to verify that the DUT is in a sane, known good state that we can run
212 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700213 """
Alex Miller1968edf2014-02-27 18:11:36 -0800214
215 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800216 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000217 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
218 # is stable in lab (destiny). The power_RPMTest failure led to reset job
219 # failure and that left dut in Repair Failed. Since the test will fail
220 # anyway due to the destiny lab issue, and test retry will retry the
221 # test in another DUT.
222 # This change temporarily disable the RPM check in reset job.
223 # Another way to do this is to remove rpm dependency from tests' control
224 # file. That will involve changes on multiple control files. This one
225 # line change here is a simple temporary fix.
226 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800227 }
228
229 name = 'verify'
230
231
232class Provision(_SpecialTaskAction):
233 """
234 Provisioning runs to change the configuration of the DUT from one state to
235 another. It will only be run on verified DUTs.
236 """
237
Dan Shi7279a5a2016-04-07 11:04:28 -0700238 # ChromeOS update must happen before firmware install, so the dut has the
239 # correct ChromeOS version label when firmware install occurs. The ChromeOS
240 # version label is used for firmware update to stage desired ChromeOS image
241 # on to the servo USB stick.
242 _priorities = [CROS_VERSION_PREFIX,
243 FW_RO_VERSION_PREFIX,
244 FW_RW_VERSION_PREFIX]
245
Alex Miller1968edf2014-02-27 18:11:36 -0800246 # TODO(milleral): http://crbug.com/249555
247 # Create some way to discover and register provisioning tests so that we
248 # don't need to hand-maintain a list of all of them.
249 _actions = {
Fang Deng9d548742015-02-03 11:35:02 -0800250 CROS_VERSION_PREFIX: actionables.TestActionable(
251 'provision_AutoUpdate',
252 extra_kwargs={'disable_sysinfo': False,
253 'disable_before_test_sysinfo': False,
254 'disable_before_iteration_sysinfo': True,
255 'disable_after_test_sysinfo': True,
256 'disable_after_iteration_sysinfo': True}),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800257 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800258 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800259 FW_RW_VERSION_PREFIX: actionables.TestActionable(
260 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700261 extra_kwargs={'rw_only': True,
262 'tag': 'rw_only'}),
Simran Basi5ace6f22016-01-06 17:30:44 -0800263 ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
264 'provision_AndroidUpdate'),
Simran Basiadf31312016-06-28 14:23:05 -0700265 TESTBED_BUILD_VERSION_PREFIX : actionables.TestActionable(
266 'provision_TestbedUpdate'),
Alex Miller1968edf2014-02-27 18:11:36 -0800267 }
268
269 name = 'provision'
270
271
272class Cleanup(_SpecialTaskAction):
273 """
274 Cleanup runs after a test fails to try and remove artifacts of tests and
275 ensure the DUT will be in a sane state for the next test run.
276 """
277
278 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800279 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800280 }
281
282 name = 'cleanup'
283
284
285class Repair(_SpecialTaskAction):
286 """
287 Repair runs when one of the other special tasks fails. It should be able
288 to take a component of the DUT that's in an unknown state and restore it to
289 a good state.
290 """
291
292 _actions = {
293 }
294
295 name = 'repair'
296
297
Alex Milleraa772002014-04-10 17:51:21 -0700298# TODO(milleral): crbug.com/364273
299# Label doesn't really mean label in this context. We're putting things into
300# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
301# doing that.
302def is_for_special_action(label):
303 """
304 If any special task handles the label specially, then we're using the label
305 to communicate that we want an action, and not as an actual dependency that
306 the test has.
307
308 @param label: A string label name.
309 @return True if any special task handles this label specially,
310 False if no special task handles this label.
311 """
312 return (Verify.acts_on(label) or
313 Provision.acts_on(label) or
314 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800315 Repair.acts_on(label) or
316 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700317
318
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000319def join(provision_type, provision_value):
320 """
321 Combine the provision type and value into the label name.
322
323 @param provision_type: One of the constants that are the label prefixes.
324 @param provision_value: A string of the value for this provision type.
325 @returns: A string that is the label name for this (type, value) pair.
326
327 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
328 'cros-version:lumpy-release/R27-3773.0.0'
329
330 """
331 return '%s:%s' % (provision_type, provision_value)
332
333
Alex Miller667b5f22014-02-28 15:33:39 -0800334class SpecialTaskActionException(Exception):
335 """
336 Exception raised when a special task fails to successfully run a test that
337 is required.
338
339 This is also a literally meaningless exception. It's always just discarded.
340 """
341
342
343def run_special_task_actions(job, host, labels, task):
344 """
345 Iterate through all `label`s and run any tests on `host` that `task` has
346 corresponding to the passed in labels.
347
348 Emits status lines for each run test, and INFO lines for each skipped label.
349
350 @param job: A job object from a control file.
351 @param host: The host to run actions on.
352 @param labels: The list of job labels to work on.
353 @param task: An instance of _SpecialTaskAction.
354 @returns: None
355 @raises: SpecialTaskActionException if a test fails.
356
357 """
Dan Shi7279a5a2016-04-07 11:04:28 -0700358 capabilities, configurations = task.partition(labels)
Alex Miller667b5f22014-02-28 15:33:39 -0800359
360 for label in capabilities:
Dan Shi7279a5a2016-04-07 11:04:28 -0700361 job.record('INFO', None, task.name,
362 "Can't %s label '%s'." % (task.name, label))
Alex Miller667b5f22014-02-28 15:33:39 -0800363
Dan Shi7279a5a2016-04-07 11:04:28 -0700364 # Sort the configuration labels based on `task._priorities`.
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000365 sorted_configurations = task.sort_configurations(configurations)
366 for name, value in sorted_configurations:
Dan Shi7279a5a2016-04-07 11:04:28 -0700367 action_item = task.test_for(name)
368 success = action_item.execute(job=job, host=host, value=value)
369 if not success:
370 raise SpecialTaskActionException()