blob: bb70710bdb467450374da2061d27fd11a03e04b1 [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
Allen Li89711f72017-02-21 18:21:52 -080020FW_RW_VERSION_PREFIX = Key.FIRMWARE_RW_VERSION
21FW_RO_VERSION_PREFIX = Key.FIRMWARE_RO_VERSION
Alex Miller0516e4c2013-06-03 18:07:48 -070022
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070023# So far the word cheets is only way to distinguish between ARC and Android
24# build.
25_ANDROID_BUILD_REGEX = r'.+/(?!cheets).+/P?([0-9]+|LATEST)'
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070026_CROS_ANDROID_BUILD_REGEX = r'.+/(?=cheets).+/P?([0-9]+|LATEST)'
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
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070031# Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning.
32CHEETS_SUFFIX = '-cheetsth'
33
Richard Barnette71854c72018-03-30 14:22:09 -070034
Allen Li606673f2017-02-21 18:41:13 -080035_Action = collections.namedtuple('_Action', 'name, value')
36
37
38def _get_label_action(str_label):
39 """Get action represented by the label.
40
41 This is used for determine actions to perform based on labels, for
42 example for provisioning or repair.
43
44 @param str_label: label string
45 @returns: _Action instance
46 """
47 try:
48 keyval_label = labellib.parse_keyval_label(str_label)
49 except ValueError:
50 return _Action(str_label, None)
51 else:
52 return _Action(keyval_label.key, keyval_label.value)
53
54
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +000055### Helpers to convert value to label
Richard Barnette6c2b70a2017-01-26 13:40:51 -080056def get_version_label_prefix(image):
57 """
58 Determine a version label prefix from a given image name.
59
60 Parses `image` to determine what kind of image it refers
61 to, and returns the corresponding version label prefix.
62
63 Known version label prefixes are:
64 * `CROS_VERSION_PREFIX` for Chrome OS version strings.
65 These images have names like `cave-release/R57-9030.0.0`.
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070066 * `CROS_ANDROID_VERSION_PREFIX` for Chrome OS Android version strings.
67 These images have names like `git_nyc-arc/cheets_x86-user/3512523`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080068 * `ANDROID_BUILD_VERSION_PREFIX` for Android build versions
69 These images have names like
70 `git_mnc-release/shamu-userdebug/2457013`.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080071
72 @param image: The image name to be parsed.
73 @returns: A string that is the prefix of version labels for the type
74 of image identified by `image`.
75
76 """
Richard Barnette17fa7c42018-04-26 00:33:11 +000077 if re.match(_ANDROID_BUILD_REGEX, image, re.I):
Richard Barnette6c2b70a2017-01-26 13:40:51 -080078 return ANDROID_BUILD_VERSION_PREFIX
Rohit Makasanadf0a3a32017-06-30 13:55:18 -070079 elif re.match(_CROS_ANDROID_BUILD_REGEX, image, re.I):
80 return CROS_ANDROID_VERSION_PREFIX
Richard Barnette6c2b70a2017-01-26 13:40:51 -080081 else:
82 return CROS_VERSION_PREFIX
83
84
85def image_version_to_label(image):
86 """
87 Return a version label appropriate to the given image name.
88
89 The type of version label is as determined described for
90 `get_version_label_prefix()`, meaning the label will identify a
Richard Barnette17fa7c42018-04-26 00:33:11 +000091 CrOS or Android version.
Richard Barnette6c2b70a2017-01-26 13:40:51 -080092
93 @param image: The image name to be parsed.
94 @returns: A string that is the appropriate label name.
95
96 """
97 return get_version_label_prefix(image) + ':' + image
98
99
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000100def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -0700101 """
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000102 Returns the proper label name for a RO firmware build of |image|.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800103
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.
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800106
107 """
Allen Liaaabda82017-02-21 18:27:38 -0800108 warnings.warn('fwro_version_to_label is deprecated', stacklevel=2)
109 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image)
110 return labellib.format_keyval_label(keyval_label)
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +0800111
112
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000113def fwrw_version_to_label(image):
114 """
115 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700116
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000117 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
118 @returns: A string that is the appropriate label name.
Dan Shi9cb0eec2014-06-03 09:04:50 -0700119
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000120 """
Allen Liaaabda82017-02-21 18:27:38 -0800121 warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2)
122 keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image)
123 return labellib.format_keyval_label(keyval_label)
Dan Shi9cb0eec2014-06-03 09:04:50 -0700124
125
Alex Miller1968edf2014-02-27 18:11:36 -0800126class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -0700127 """
Alex Miller1968edf2014-02-27 18:11:36 -0800128 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -0700129 """
Alex Miller1968edf2014-02-27 18:11:36 -0800130
Dan Shi7279a5a2016-04-07 11:04:28 -0700131 # A dictionary mapping labels to test names.
132 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -0700133
Dan Shi7279a5a2016-04-07 11:04:28 -0700134 # The name of this special task to be used in output.
135 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -0700136
Dan Shi7279a5a2016-04-07 11:04:28 -0700137 # Some special tasks require to run before others, e.g., ChromeOS image
138 # needs to be updated before firmware provision. List `_priorities` defines
139 # the order of each label prefix. An element with a smaller index has higher
140 # priority. Not listed ones have the lowest priority.
141 # This property should be overriden in subclass to define its own priorities
142 # across available label prefixes.
143 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -0800144
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000145
Alex Miller1968edf2014-02-27 18:11:36 -0800146 @classmethod
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000147 def acts_on(cls, label):
Alex Miller1968edf2014-02-27 18:11:36 -0800148 """
149 Returns True if the label is a label that we recognize as something we
150 know how to act on, given our _actions.
151
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000152 @param label: The label as a string.
Alex Miller1968edf2014-02-27 18:11:36 -0800153 @returns: True if there exists a test to run for this label.
Alex Miller1968edf2014-02-27 18:11:36 -0800154 """
Allen Li95e930a2017-02-21 18:31:18 -0800155 action = _get_label_action(label)
156 return action.name in cls._actions
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000157
Alex Miller1968edf2014-02-27 18:11:36 -0800158
159 @classmethod
Allen Li21154582017-02-22 18:00:42 -0800160 def run_task_actions(cls, job, host, labels):
161 """
162 Run task actions on host that correspond to the labels.
163
164 Emits status lines for each run test, and INFO lines for each
165 skipped label.
166
167 @param job: A job object from a control file.
168 @param host: The host to run actions on.
169 @param labels: The list of job labels to work on.
170 @raises: SpecialTaskActionException if a test fails.
171 """
Allen Lid575f642017-05-23 13:07:28 -0700172 unactionable = cls._filter_unactionable_labels(labels)
Allen Li21154582017-02-22 18:00:42 -0800173 for label in unactionable:
174 job.record('INFO', None, cls.name,
175 "Can't %s label '%s'." % (cls.name, label))
176
Allen Lid575f642017-05-23 13:07:28 -0700177 for action_item, value in cls._actions_and_values_iter(labels):
Allen Li21154582017-02-22 18:00:42 -0800178 success = action_item.execute(job=job, host=host, value=value)
179 if not success:
180 raise SpecialTaskActionException()
181
182
183 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700184 def _actions_and_values_iter(cls, labels):
185 """Return sorted action and value pairs to run for labels.
Alex Miller1968edf2014-02-27 18:11:36 -0800186
Allen Lid575f642017-05-23 13:07:28 -0700187 @params: An iterable of label strings.
188 @returns: A generator of Actionable and value pairs.
Alex Miller1968edf2014-02-27 18:11:36 -0800189 """
Allen Lid575f642017-05-23 13:07:28 -0700190 actionable = cls._filter_actionable_labels(labels)
191 keyval_mapping = labellib.LabelsMapping(actionable)
192 sorted_names = sorted(keyval_mapping, key=cls._get_action_priority)
193 for name in sorted_names:
194 action_item = cls._actions[name]
195 value = keyval_mapping[name]
196 yield action_item, value
197
198
199 @classmethod
200 def _filter_unactionable_labels(cls, labels):
201 """
202 Return labels that we cannot act on.
203
204 @param labels: A list of strings of labels.
205 @returns: A set of unactionable labels
206 """
207 return {label for label in labels
208 if not (label == SKIP_PROVISION or cls.acts_on(label))}
209
210
211 @classmethod
212 def _filter_actionable_labels(cls, labels):
213 """
214 Return labels that we can act on.
215
216 @param labels: A list of strings of labels.
217 @returns: A set of actionable labels
218 """
219 return {label for label in labels if cls.acts_on(label)}
Alex Miller1968edf2014-02-27 18:11:36 -0800220
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000221
Alex Milleraa772002014-04-10 17:51:21 -0700222 @classmethod
223 def partition(cls, labels):
224 """
225 Filter a list of labels into two sets: those labels that we know how to
226 act on and those that we don't know how to act on.
227
228 @param labels: A list of strings of labels.
229 @returns: A tuple where the first element is a set of unactionable
230 labels, and the second element is a set of the actionable
231 labels.
232 """
Allen Li5b623122017-02-22 17:42:02 -0800233 unactionable = set()
234 actionable = set()
Alex Milleraa772002014-04-10 17:51:21 -0700235
236 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800237 if label == SKIP_PROVISION:
238 # skip_provision is neither actionable or a capability label.
239 # It doesn't need any handling.
240 continue
241 elif cls.acts_on(label):
Allen Li5b623122017-02-22 17:42:02 -0800242 actionable.add(label)
Alex Milleraa772002014-04-10 17:51:21 -0700243 else:
Allen Li5b623122017-02-22 17:42:02 -0800244 unactionable.add(label)
Rohit Makasanac4eacc92017-09-06 13:49:38 -0700245
Allen Li5b623122017-02-22 17:42:02 -0800246 return unactionable, actionable
Alex Milleraa772002014-04-10 17:51:21 -0700247
Prathmesh Prabhu2c7471d2016-11-15 20:19:57 +0000248
Dan Shi7279a5a2016-04-07 11:04:28 -0700249 @classmethod
Allen Lid575f642017-05-23 13:07:28 -0700250 def _get_action_priority(cls, name):
251 """Return priority for the action with the given name."""
252 if name in cls._priorities:
253 return cls._priorities.index(name)
254 else:
255 return sys.maxint
Dan Shi7279a5a2016-04-07 11:04:28 -0700256
257
Alex Miller1968edf2014-02-27 18:11:36 -0800258class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700259 """
Alex Miller1968edf2014-02-27 18:11:36 -0800260 Tests to verify that the DUT is in a sane, known good state that we can run
261 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700262 """
Alex Miller1968edf2014-02-27 18:11:36 -0800263
264 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800265 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000266 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
267 # is stable in lab (destiny). The power_RPMTest failure led to reset job
268 # failure and that left dut in Repair Failed. Since the test will fail
269 # anyway due to the destiny lab issue, and test retry will retry the
270 # test in another DUT.
271 # This change temporarily disable the RPM check in reset job.
272 # Another way to do this is to remove rpm dependency from tests' control
273 # file. That will involve changes on multiple control files. This one
274 # line change here is a simple temporary fix.
275 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800276 }
277
278 name = 'verify'
279
280
281class Provision(_SpecialTaskAction):
282 """
283 Provisioning runs to change the configuration of the DUT from one state to
284 another. It will only be run on verified DUTs.
285 """
286
Dan Shi7279a5a2016-04-07 11:04:28 -0700287 # ChromeOS update must happen before firmware install, so the dut has the
288 # correct ChromeOS version label when firmware install occurs. The ChromeOS
289 # version label is used for firmware update to stage desired ChromeOS image
290 # on to the servo USB stick.
291 _priorities = [CROS_VERSION_PREFIX,
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700292 CROS_ANDROID_VERSION_PREFIX,
Dan Shi7279a5a2016-04-07 11:04:28 -0700293 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}),
Rohit Makasanadf0a3a32017-06-30 13:55:18 -0700307 CROS_ANDROID_VERSION_PREFIX : actionables.TestActionable(
Rohit Makasana37f5cf02017-06-08 17:21:25 -0700308 'provision_CheetsUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800309 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800310 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800311 FW_RW_VERSION_PREFIX: actionables.TestActionable(
312 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700313 extra_kwargs={'rw_only': True,
314 'tag': 'rw_only'}),
Simran Basi5ace6f22016-01-06 17:30:44 -0800315 ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
316 'provision_AndroidUpdate'),
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)