blob: cc9c9317b14f17c3e1c62695a6d47acda75f8ba2 [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
5
Dan Shi7279a5a2016-04-07 11:04:28 -07006import sys
Alex Miller0516e4c2013-06-03 18:07:48 -07007
8import common
Fang Dengaf30e7c2014-11-15 13:57:03 -08009from autotest_lib.server.cros import provision_actionables as actionables
Alex Miller0516e4c2013-06-03 18:07:48 -070010
11
12### Constants for label prefixes
13CROS_VERSION_PREFIX = 'cros-version'
Simran Basi5ace6f22016-01-06 17:30:44 -080014ANDROID_BUILD_VERSION_PREFIX = 'ab-version'
Dan Shi0723bf52015-06-24 10:52:38 -070015FW_RW_VERSION_PREFIX = 'fwrw-version'
Dan Shi36cfd832014-10-10 13:38:51 -070016FW_RO_VERSION_PREFIX = 'fwro-version'
Alex Miller0516e4c2013-06-03 18:07:48 -070017
Dan Shie44f9c02016-02-18 13:25:05 -080018# Special label to skip provision and run reset instead.
19SKIP_PROVISION = 'skip_provision'
20
Chris Sosae92399e2015-04-24 11:32:59 -070021# Default number of provisions attempts to try if we believe the devserver is
22# flaky.
23FLAKY_DEVSERVER_ATTEMPTS = 2
24
Alex Miller0516e4c2013-06-03 18:07:48 -070025
26### Helpers to convert value to label
27def cros_version_to_label(image):
28 """
29 Returns the proper label name for a ChromeOS build of |image|.
30
31 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
32 @returns: A string that is the appropriate label name.
33
34 """
35 return CROS_VERSION_PREFIX + ':' + image
36
37
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080038def fwro_version_to_label(image):
Dan Shi9cb0eec2014-06-03 09:04:50 -070039 """
Tom Wai-Hong Tam2d00cb22016-01-08 06:40:50 +080040 Returns the proper label name for a RO firmware build of |image|.
41
42 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
43 @returns: A string that is the appropriate label name.
44
45 """
46 return FW_RO_VERSION_PREFIX + ':' + image
47
48
49def fwrw_version_to_label(image):
50 """
51 Returns the proper label name for a RW firmware build of |image|.
Dan Shi9cb0eec2014-06-03 09:04:50 -070052
53 @param image: A string of the form 'lumpy-release/R28-3993.0.0'
54 @returns: A string that is the appropriate label name.
55
56 """
Dan Shi0723bf52015-06-24 10:52:38 -070057 return FW_RW_VERSION_PREFIX + ':' + image
Dan Shi9cb0eec2014-06-03 09:04:50 -070058
59
Alex Miller1968edf2014-02-27 18:11:36 -080060class _SpecialTaskAction(object):
Alex Miller0516e4c2013-06-03 18:07:48 -070061 """
Alex Miller1968edf2014-02-27 18:11:36 -080062 Base class to give a template for mapping labels to tests.
Alex Miller0516e4c2013-06-03 18:07:48 -070063 """
Alex Miller1968edf2014-02-27 18:11:36 -080064
Dan Shi7279a5a2016-04-07 11:04:28 -070065 # A dictionary mapping labels to test names.
66 _actions = {}
Alex Miller0516e4c2013-06-03 18:07:48 -070067
Dan Shi7279a5a2016-04-07 11:04:28 -070068 # The name of this special task to be used in output.
69 name = None;
Alex Miller0516e4c2013-06-03 18:07:48 -070070
Dan Shi7279a5a2016-04-07 11:04:28 -070071 # Some special tasks require to run before others, e.g., ChromeOS image
72 # needs to be updated before firmware provision. List `_priorities` defines
73 # the order of each label prefix. An element with a smaller index has higher
74 # priority. Not listed ones have the lowest priority.
75 # This property should be overriden in subclass to define its own priorities
76 # across available label prefixes.
77 _priorities = []
Alex Miller1968edf2014-02-27 18:11:36 -080078
79
80 @classmethod
81 def acts_on(cls, label):
82 """
83 Returns True if the label is a label that we recognize as something we
84 know how to act on, given our _actions.
85
86 @param label: The label as a string.
87 @returns: True if there exists a test to run for this label.
88
89 """
90 return label.split(':')[0] in cls._actions
91
92
93 @classmethod
94 def test_for(cls, label):
95 """
96 Returns the test associated with the given (string) label name.
97
98 @param label: The label for which the action is being requested.
99 @returns: The string name of the test that should be run.
100 @raises KeyError: If the name was not recognized as one we care about.
101
102 """
103 return cls._actions[label]
104
105
Alex Milleraa772002014-04-10 17:51:21 -0700106 @classmethod
107 def partition(cls, labels):
108 """
109 Filter a list of labels into two sets: those labels that we know how to
110 act on and those that we don't know how to act on.
111
112 @param labels: A list of strings of labels.
113 @returns: A tuple where the first element is a set of unactionable
114 labels, and the second element is a set of the actionable
115 labels.
116 """
117 capabilities = set()
118 configurations = set()
119
120 for label in labels:
Dan Shie44f9c02016-02-18 13:25:05 -0800121 if label == SKIP_PROVISION:
122 # skip_provision is neither actionable or a capability label.
123 # It doesn't need any handling.
124 continue
125 elif cls.acts_on(label):
Alex Milleraa772002014-04-10 17:51:21 -0700126 configurations.add(label)
127 else:
128 capabilities.add(label)
129
130 return capabilities, configurations
131
132
Dan Shi7279a5a2016-04-07 11:04:28 -0700133 @classmethod
134 def sort_configurations(cls, configurations):
135 """
136 Sort configurations based on the priority defined in cls._priorities.
137
138 @param configurations: A list of actionable labels.
139
140 @return: A sorted list of tuple of (label_prefix, value), the tuples are
141 sorted based on the label_prefix's index in cls._priorities.
142 """
143 # Split a list of labels into a dict mapping name to value. All labels
144 # must be provisionable labels, or else a ValueError
145 # For example, label 'cros-version:lumpy-release/R28-3993.0.0' is split
146 # to {'cros-version': 'lumpy-release/R28-3993.0.0'}
147 split_configurations = dict()
148 for label in configurations:
149 name, _, value = label.partition(':')
150 split_configurations[name] = value
151
152 sort_key = (lambda config:
153 (cls._priorities.index(config[0])
154 if (config[0] in cls._priorities) else sys.maxint))
155 return sorted(split_configurations.items(), key=sort_key)
156
157
Alex Miller1968edf2014-02-27 18:11:36 -0800158class Verify(_SpecialTaskAction):
Alex Miller0516e4c2013-06-03 18:07:48 -0700159 """
Alex Miller1968edf2014-02-27 18:11:36 -0800160 Tests to verify that the DUT is in a sane, known good state that we can run
161 tests on. Failure to verify leads to running Repair.
Alex Miller0516e4c2013-06-03 18:07:48 -0700162 """
Alex Miller1968edf2014-02-27 18:11:36 -0800163
164 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800165 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
Don Garrett6f7e8002015-07-23 22:45:37 +0000166 # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
167 # is stable in lab (destiny). The power_RPMTest failure led to reset job
168 # failure and that left dut in Repair Failed. Since the test will fail
169 # anyway due to the destiny lab issue, and test retry will retry the
170 # test in another DUT.
171 # This change temporarily disable the RPM check in reset job.
172 # Another way to do this is to remove rpm dependency from tests' control
173 # file. That will involve changes on multiple control files. This one
174 # line change here is a simple temporary fix.
175 'rpm': actionables.TestActionable('dummy_PassServer'),
Alex Miller1968edf2014-02-27 18:11:36 -0800176 }
177
178 name = 'verify'
179
180
181class Provision(_SpecialTaskAction):
182 """
183 Provisioning runs to change the configuration of the DUT from one state to
184 another. It will only be run on verified DUTs.
185 """
186
Dan Shi7279a5a2016-04-07 11:04:28 -0700187 # ChromeOS update must happen before firmware install, so the dut has the
188 # correct ChromeOS version label when firmware install occurs. The ChromeOS
189 # version label is used for firmware update to stage desired ChromeOS image
190 # on to the servo USB stick.
191 _priorities = [CROS_VERSION_PREFIX,
192 FW_RO_VERSION_PREFIX,
193 FW_RW_VERSION_PREFIX]
194
Alex Miller1968edf2014-02-27 18:11:36 -0800195 # TODO(milleral): http://crbug.com/249555
196 # Create some way to discover and register provisioning tests so that we
197 # don't need to hand-maintain a list of all of them.
198 _actions = {
Fang Deng9d548742015-02-03 11:35:02 -0800199 CROS_VERSION_PREFIX: actionables.TestActionable(
200 'provision_AutoUpdate',
201 extra_kwargs={'disable_sysinfo': False,
202 'disable_before_test_sysinfo': False,
203 'disable_before_iteration_sysinfo': True,
204 'disable_after_test_sysinfo': True,
205 'disable_after_iteration_sysinfo': True}),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800206 FW_RO_VERSION_PREFIX: actionables.TestActionable(
Fang Dengaf30e7c2014-11-15 13:57:03 -0800207 'provision_FirmwareUpdate'),
Tom Wai-Hong Tam9a237612016-01-08 03:41:46 +0800208 FW_RW_VERSION_PREFIX: actionables.TestActionable(
209 'provision_FirmwareUpdate',
Dan Shi61e407c2016-04-08 14:21:07 -0700210 extra_kwargs={'rw_only': True,
211 'tag': 'rw_only'}),
Simran Basi5ace6f22016-01-06 17:30:44 -0800212 ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
213 'provision_AndroidUpdate'),
Alex Miller1968edf2014-02-27 18:11:36 -0800214 }
215
216 name = 'provision'
217
218
219class Cleanup(_SpecialTaskAction):
220 """
221 Cleanup runs after a test fails to try and remove artifacts of tests and
222 ensure the DUT will be in a sane state for the next test run.
223 """
224
225 _actions = {
Fang Dengaf30e7c2014-11-15 13:57:03 -0800226 'cleanup-reboot': actionables.RebootActionable(),
Alex Miller1968edf2014-02-27 18:11:36 -0800227 }
228
229 name = 'cleanup'
230
231
232class Repair(_SpecialTaskAction):
233 """
234 Repair runs when one of the other special tasks fails. It should be able
235 to take a component of the DUT that's in an unknown state and restore it to
236 a good state.
237 """
238
239 _actions = {
240 }
241
242 name = 'repair'
243
244
Alex Miller1968edf2014-02-27 18:11:36 -0800245
Alex Milleraa772002014-04-10 17:51:21 -0700246# TODO(milleral): crbug.com/364273
247# Label doesn't really mean label in this context. We're putting things into
248# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
249# doing that.
250def is_for_special_action(label):
251 """
252 If any special task handles the label specially, then we're using the label
253 to communicate that we want an action, and not as an actual dependency that
254 the test has.
255
256 @param label: A string label name.
257 @return True if any special task handles this label specially,
258 False if no special task handles this label.
259 """
260 return (Verify.acts_on(label) or
261 Provision.acts_on(label) or
262 Cleanup.acts_on(label) or
Dan Shie44f9c02016-02-18 13:25:05 -0800263 Repair.acts_on(label) or
264 label == SKIP_PROVISION)
Alex Miller0516e4c2013-06-03 18:07:48 -0700265
266
Alex Miller2229c9c2013-08-27 15:20:39 -0700267def join(provision_type, provision_value):
268 """
269 Combine the provision type and value into the label name.
270
271 @param provision_type: One of the constants that are the label prefixes.
272 @param provision_value: A string of the value for this provision type.
273 @returns: A string that is the label name for this (type, value) pair.
274
275 >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
276 'cros-version:lumpy-release/R27-3773.0.0'
277
278 """
279 return '%s:%s' % (provision_type, provision_value)
280
281
Alex Miller667b5f22014-02-28 15:33:39 -0800282class SpecialTaskActionException(Exception):
283 """
284 Exception raised when a special task fails to successfully run a test that
285 is required.
286
287 This is also a literally meaningless exception. It's always just discarded.
288 """
289
290
291def run_special_task_actions(job, host, labels, task):
292 """
293 Iterate through all `label`s and run any tests on `host` that `task` has
294 corresponding to the passed in labels.
295
296 Emits status lines for each run test, and INFO lines for each skipped label.
297
298 @param job: A job object from a control file.
299 @param host: The host to run actions on.
300 @param labels: The list of job labels to work on.
301 @param task: An instance of _SpecialTaskAction.
302 @returns: None
303 @raises: SpecialTaskActionException if a test fails.
304
305 """
Dan Shi7279a5a2016-04-07 11:04:28 -0700306 capabilities, configurations = task.partition(labels)
Alex Miller667b5f22014-02-28 15:33:39 -0800307
308 for label in capabilities:
Dan Shi7279a5a2016-04-07 11:04:28 -0700309 job.record('INFO', None, task.name,
310 "Can't %s label '%s'." % (task.name, label))
Alex Miller667b5f22014-02-28 15:33:39 -0800311
Dan Shi7279a5a2016-04-07 11:04:28 -0700312 # Sort the configuration labels based on `task._priorities`.
313 sorted_configurations = task.sort_configurations(configurations)
314 for name, value in sorted_configurations:
315 action_item = task.test_for(name)
316 success = action_item.execute(job=job, host=host, value=value)
317 if not success:
318 raise SpecialTaskActionException()