1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport collections 7*9c5db199SXin Liimport re 8*9c5db199SXin Liimport sys 9*9c5db199SXin Liimport warnings 10*9c5db199SXin Li 11*9c5db199SXin Liimport common 12*9c5db199SXin Lifrom autotest_lib.server.cros import provision_actionables as actionables 13*9c5db199SXin Lifrom autotest_lib.utils import labellib 14*9c5db199SXin Lifrom autotest_lib.utils.labellib import Key 15*9c5db199SXin Li 16*9c5db199SXin Li 17*9c5db199SXin Li### Constants for label prefixes 18*9c5db199SXin LiCROS_VERSION_PREFIX = Key.CROS_VERSION 19*9c5db199SXin LiCROS_ANDROID_VERSION_PREFIX = Key.CROS_ANDROID_VERSION 20*9c5db199SXin LiFW_RW_VERSION_PREFIX = Key.FIRMWARE_RW_VERSION 21*9c5db199SXin LiFW_RO_VERSION_PREFIX = Key.FIRMWARE_RO_VERSION 22*9c5db199SXin LiFW_CR50_RW_VERSION_PREFIX = Key.FIRMWARE_CR50_RW_VERSION 23*9c5db199SXin Li 24*9c5db199SXin Li# So far the word cheets is only way to distinguish between ARC and Android 25*9c5db199SXin Li# build. 26*9c5db199SXin Li_CROS_ANDROID_BUILD_REGEX = r'.+/cheets.*/P?([0-9]+|LATEST)' 27*9c5db199SXin Li 28*9c5db199SXin Li# Special label to skip provision and run reset instead. 29*9c5db199SXin LiSKIP_PROVISION = 'skip_provision' 30*9c5db199SXin Li 31*9c5db199SXin Li# Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning. 32*9c5db199SXin LiCHEETS_SUFFIX = '-cheetsth' 33*9c5db199SXin Li 34*9c5db199SXin Li# ChromeOS image archive server address 35*9c5db199SXin LiCROS_IMAGE_ARCHIVE = 'gs://chromeos-image-archive' 36*9c5db199SXin Li 37*9c5db199SXin Li# ChromeOS firmware branch directory name. %s is for a (base)board name. 38*9c5db199SXin LiFW_BRANCH_GLOB = 'firmware-%s-[0-9]*.B-firmwarebranch' 39*9c5db199SXin Li 40*9c5db199SXin Li_Action = collections.namedtuple('_Action', 'name, value') 41*9c5db199SXin Li 42*9c5db199SXin Li 43*9c5db199SXin Lidef _get_label_action(str_label): 44*9c5db199SXin Li """Get action represented by the label. 45*9c5db199SXin Li 46*9c5db199SXin Li This is used for determine actions to perform based on labels, for 47*9c5db199SXin Li example for provisioning or repair. 48*9c5db199SXin Li 49*9c5db199SXin Li @param str_label: label string 50*9c5db199SXin Li @returns: _Action instance 51*9c5db199SXin Li """ 52*9c5db199SXin Li try: 53*9c5db199SXin Li keyval_label = labellib.parse_keyval_label(str_label) 54*9c5db199SXin Li except ValueError: 55*9c5db199SXin Li return _Action(str_label, None) 56*9c5db199SXin Li else: 57*9c5db199SXin Li return _Action(keyval_label.key, keyval_label.value) 58*9c5db199SXin Li 59*9c5db199SXin Li 60*9c5db199SXin Li### Helpers to convert value to label 61*9c5db199SXin Lidef get_version_label_prefix(image): 62*9c5db199SXin Li """ 63*9c5db199SXin Li Determine a version label prefix from a given image name. 64*9c5db199SXin Li 65*9c5db199SXin Li Parses `image` to determine what kind of image it refers 66*9c5db199SXin Li to, and returns the corresponding version label prefix. 67*9c5db199SXin Li 68*9c5db199SXin Li Known version label prefixes are: 69*9c5db199SXin Li * `CROS_VERSION_PREFIX` for ChromeOS version strings. 70*9c5db199SXin Li These images have names like `cave-release/R57-9030.0.0`. 71*9c5db199SXin Li * `CROS_ANDROID_VERSION_PREFIX` for ChromeOS Android version strings. 72*9c5db199SXin Li These images have names like `git_nyc-arc/cheets_x86-user/3512523`. 73*9c5db199SXin Li 74*9c5db199SXin Li @param image: The image name to be parsed. 75*9c5db199SXin Li @returns: A string that is the prefix of version labels for the type 76*9c5db199SXin Li of image identified by `image`. 77*9c5db199SXin Li 78*9c5db199SXin Li """ 79*9c5db199SXin Li if re.match(_CROS_ANDROID_BUILD_REGEX, image, re.I): 80*9c5db199SXin Li return CROS_ANDROID_VERSION_PREFIX 81*9c5db199SXin Li else: 82*9c5db199SXin Li return CROS_VERSION_PREFIX 83*9c5db199SXin Li 84*9c5db199SXin Li 85*9c5db199SXin Lidef image_version_to_label(image): 86*9c5db199SXin Li """ 87*9c5db199SXin Li Return a version label appropriate to the given image name. 88*9c5db199SXin Li 89*9c5db199SXin Li The type of version label is as determined described for 90*9c5db199SXin Li `get_version_label_prefix()`, meaning the label will identify a 91*9c5db199SXin Li CrOS or Android version. 92*9c5db199SXin Li 93*9c5db199SXin Li @param image: The image name to be parsed. 94*9c5db199SXin Li @returns: A string that is the appropriate label name. 95*9c5db199SXin Li 96*9c5db199SXin Li """ 97*9c5db199SXin Li return get_version_label_prefix(image) + ':' + image 98*9c5db199SXin Li 99*9c5db199SXin Li 100*9c5db199SXin Lidef fwro_version_to_label(image): 101*9c5db199SXin Li """ 102*9c5db199SXin Li Returns the proper label name for a RO firmware build of |image|. 103*9c5db199SXin Li 104*9c5db199SXin Li @param image: A string of the form 'lumpy-release/R28-3993.0.0' 105*9c5db199SXin Li @returns: A string that is the appropriate label name. 106*9c5db199SXin Li 107*9c5db199SXin Li """ 108*9c5db199SXin Li warnings.warn('fwro_version_to_label is deprecated', stacklevel=2) 109*9c5db199SXin Li keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RO_VERSION, image) 110*9c5db199SXin Li return labellib.format_keyval_label(keyval_label) 111*9c5db199SXin Li 112*9c5db199SXin Li 113*9c5db199SXin Lidef fwrw_version_to_label(image): 114*9c5db199SXin Li """ 115*9c5db199SXin Li Returns the proper label name for a RW firmware build of |image|. 116*9c5db199SXin Li 117*9c5db199SXin Li @param image: A string of the form 'lumpy-release/R28-3993.0.0' 118*9c5db199SXin Li @returns: A string that is the appropriate label name. 119*9c5db199SXin Li 120*9c5db199SXin Li """ 121*9c5db199SXin Li warnings.warn('fwrw_version_to_label is deprecated', stacklevel=2) 122*9c5db199SXin Li keyval_label = labellib.KeyvalLabel(Key.FIRMWARE_RW_VERSION, image) 123*9c5db199SXin Li return labellib.format_keyval_label(keyval_label) 124*9c5db199SXin Li 125*9c5db199SXin Li 126*9c5db199SXin Liclass _SpecialTaskAction(object): 127*9c5db199SXin Li """ 128*9c5db199SXin Li Base class to give a template for mapping labels to tests. 129*9c5db199SXin Li """ 130*9c5db199SXin Li 131*9c5db199SXin Li # A dictionary mapping labels to test names. 132*9c5db199SXin Li _actions = {} 133*9c5db199SXin Li 134*9c5db199SXin Li # The name of this special task to be used in output. 135*9c5db199SXin Li name = None; 136*9c5db199SXin Li 137*9c5db199SXin Li # Some special tasks require to run before others, e.g., ChromeOS image 138*9c5db199SXin Li # needs to be updated before firmware provision. List `_priorities` defines 139*9c5db199SXin Li # the order of each label prefix. An element with a smaller index has higher 140*9c5db199SXin Li # priority. Not listed ones have the lowest priority. 141*9c5db199SXin Li # This property should be overriden in subclass to define its own priorities 142*9c5db199SXin Li # across available label prefixes. 143*9c5db199SXin Li _priorities = [] 144*9c5db199SXin Li 145*9c5db199SXin Li 146*9c5db199SXin Li @classmethod 147*9c5db199SXin Li def acts_on(cls, label): 148*9c5db199SXin Li """ 149*9c5db199SXin Li Returns True if the label is a label that we recognize as something we 150*9c5db199SXin Li know how to act on, given our _actions. 151*9c5db199SXin Li 152*9c5db199SXin Li @param label: The label as a string. 153*9c5db199SXin Li @returns: True if there exists a test to run for this label. 154*9c5db199SXin Li """ 155*9c5db199SXin Li action = _get_label_action(label) 156*9c5db199SXin Li return action.name in cls._actions 157*9c5db199SXin Li 158*9c5db199SXin Li 159*9c5db199SXin Li @classmethod 160*9c5db199SXin Li def run_task_actions(cls, job, host, labels): 161*9c5db199SXin Li """ 162*9c5db199SXin Li Run task actions on host that correspond to the labels. 163*9c5db199SXin Li 164*9c5db199SXin Li Emits status lines for each run test, and INFO lines for each 165*9c5db199SXin Li skipped label. 166*9c5db199SXin Li 167*9c5db199SXin Li @param job: A job object from a control file. 168*9c5db199SXin Li @param host: The host to run actions on. 169*9c5db199SXin Li @param labels: The list of job labels to work on. 170*9c5db199SXin Li @raises: SpecialTaskActionException if a test fails. 171*9c5db199SXin Li """ 172*9c5db199SXin Li unactionable = cls._filter_unactionable_labels(labels) 173*9c5db199SXin Li for label in unactionable: 174*9c5db199SXin Li job.record('INFO', None, cls.name, 175*9c5db199SXin Li "Can't %s label '%s'." % (cls.name, label)) 176*9c5db199SXin Li 177*9c5db199SXin Li for action_item, value in cls._actions_and_values_iter(labels): 178*9c5db199SXin Li success = action_item.execute(job=job, host=host, value=value) 179*9c5db199SXin Li if not success: 180*9c5db199SXin Li raise SpecialTaskActionException() 181*9c5db199SXin Li 182*9c5db199SXin Li 183*9c5db199SXin Li @classmethod 184*9c5db199SXin Li def _actions_and_values_iter(cls, labels): 185*9c5db199SXin Li """Return sorted action and value pairs to run for labels. 186*9c5db199SXin Li 187*9c5db199SXin Li @params: An iterable of label strings. 188*9c5db199SXin Li @returns: A generator of Actionable and value pairs. 189*9c5db199SXin Li """ 190*9c5db199SXin Li actionable = cls._filter_actionable_labels(labels) 191*9c5db199SXin Li keyval_mapping = labellib.LabelsMapping(actionable) 192*9c5db199SXin Li sorted_names = sorted(keyval_mapping, key=cls._get_action_priority) 193*9c5db199SXin Li for name in sorted_names: 194*9c5db199SXin Li action_item = cls._actions[name] 195*9c5db199SXin Li value = keyval_mapping[name] 196*9c5db199SXin Li yield action_item, value 197*9c5db199SXin Li 198*9c5db199SXin Li 199*9c5db199SXin Li @classmethod 200*9c5db199SXin Li def _filter_unactionable_labels(cls, labels): 201*9c5db199SXin Li """ 202*9c5db199SXin Li Return labels that we cannot act on. 203*9c5db199SXin Li 204*9c5db199SXin Li @param labels: A list of strings of labels. 205*9c5db199SXin Li @returns: A set of unactionable labels 206*9c5db199SXin Li """ 207*9c5db199SXin Li return {label for label in labels 208*9c5db199SXin Li if not (label == SKIP_PROVISION or cls.acts_on(label))} 209*9c5db199SXin Li 210*9c5db199SXin Li 211*9c5db199SXin Li @classmethod 212*9c5db199SXin Li def _filter_actionable_labels(cls, labels): 213*9c5db199SXin Li """ 214*9c5db199SXin Li Return labels that we can act on. 215*9c5db199SXin Li 216*9c5db199SXin Li @param labels: A list of strings of labels. 217*9c5db199SXin Li @returns: A set of actionable labels 218*9c5db199SXin Li """ 219*9c5db199SXin Li return {label for label in labels if cls.acts_on(label)} 220*9c5db199SXin Li 221*9c5db199SXin Li 222*9c5db199SXin Li @classmethod 223*9c5db199SXin Li def partition(cls, labels): 224*9c5db199SXin Li """ 225*9c5db199SXin Li Filter a list of labels into two sets: those labels that we know how to 226*9c5db199SXin Li act on and those that we don't know how to act on. 227*9c5db199SXin Li 228*9c5db199SXin Li @param labels: A list of strings of labels. 229*9c5db199SXin Li @returns: A tuple where the first element is a set of unactionable 230*9c5db199SXin Li labels, and the second element is a set of the actionable 231*9c5db199SXin Li labels. 232*9c5db199SXin Li """ 233*9c5db199SXin Li unactionable = set() 234*9c5db199SXin Li actionable = set() 235*9c5db199SXin Li 236*9c5db199SXin Li for label in labels: 237*9c5db199SXin Li if label == SKIP_PROVISION: 238*9c5db199SXin Li # skip_provision is neither actionable or a capability label. 239*9c5db199SXin Li # It doesn't need any handling. 240*9c5db199SXin Li continue 241*9c5db199SXin Li elif cls.acts_on(label): 242*9c5db199SXin Li actionable.add(label) 243*9c5db199SXin Li else: 244*9c5db199SXin Li unactionable.add(label) 245*9c5db199SXin Li 246*9c5db199SXin Li return unactionable, actionable 247*9c5db199SXin Li 248*9c5db199SXin Li 249*9c5db199SXin Li @classmethod 250*9c5db199SXin Li def _get_action_priority(cls, name): 251*9c5db199SXin Li """Return priority for the action with the given name.""" 252*9c5db199SXin Li if name in cls._priorities: 253*9c5db199SXin Li return cls._priorities.index(name) 254*9c5db199SXin Li else: 255*9c5db199SXin Li return sys.maxsize 256*9c5db199SXin Li 257*9c5db199SXin Li 258*9c5db199SXin Liclass Verify(_SpecialTaskAction): 259*9c5db199SXin Li """ 260*9c5db199SXin Li Tests to verify that the DUT is in a known good state that we can run 261*9c5db199SXin Li tests on. Failure to verify leads to running Repair. 262*9c5db199SXin Li """ 263*9c5db199SXin Li 264*9c5db199SXin Li _actions = { 265*9c5db199SXin Li 'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'), 266*9c5db199SXin Li # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM 267*9c5db199SXin Li # is stable in lab (destiny). The power_RPMTest failure led to reset job 268*9c5db199SXin Li # failure and that left dut in Repair Failed. Since the test will fail 269*9c5db199SXin Li # anyway due to the destiny lab issue, and test retry will retry the 270*9c5db199SXin Li # test in another DUT. 271*9c5db199SXin Li # This change temporarily disable the RPM check in reset job. 272*9c5db199SXin Li # Another way to do this is to remove rpm dependency from tests' control 273*9c5db199SXin Li # file. That will involve changes on multiple control files. This one 274*9c5db199SXin Li # line change here is a simple temporary fix. 275*9c5db199SXin Li 'rpm': actionables.TestActionable('stub_PassServer'), 276*9c5db199SXin Li } 277*9c5db199SXin Li 278*9c5db199SXin Li name = 'verify' 279*9c5db199SXin Li 280*9c5db199SXin Li 281*9c5db199SXin Liclass Provision(_SpecialTaskAction): 282*9c5db199SXin Li """ 283*9c5db199SXin Li Provisioning runs to change the configuration of the DUT from one state to 284*9c5db199SXin Li another. It will only be run on verified DUTs. 285*9c5db199SXin Li """ 286*9c5db199SXin Li 287*9c5db199SXin Li # ChromeOS update must happen before firmware install, so the dut has the 288*9c5db199SXin Li # correct ChromeOS version label when firmware install occurs. The ChromeOS 289*9c5db199SXin Li # version label is used for firmware update to stage desired ChromeOS image 290*9c5db199SXin Li # on to the servo USB stick. 291*9c5db199SXin Li _priorities = [CROS_VERSION_PREFIX, 292*9c5db199SXin Li CROS_ANDROID_VERSION_PREFIX, 293*9c5db199SXin Li FW_RO_VERSION_PREFIX, 294*9c5db199SXin Li FW_RW_VERSION_PREFIX, 295*9c5db199SXin Li FW_CR50_RW_VERSION_PREFIX] 296*9c5db199SXin Li 297*9c5db199SXin Li # TODO(milleral): http://crbug.com/249555 298*9c5db199SXin Li # Create some way to discover and register provisioning tests so that we 299*9c5db199SXin Li # don't need to hand-maintain a list of all of them. 300*9c5db199SXin Li _actions = { 301*9c5db199SXin Li CROS_VERSION_PREFIX: 302*9c5db199SXin Li actionables.TestActionable( 303*9c5db199SXin Li 'provision_QuickProvision', 304*9c5db199SXin Li extra_kwargs={ 305*9c5db199SXin Li 'disable_sysinfo': False, 306*9c5db199SXin Li 'disable_before_test_sysinfo': False, 307*9c5db199SXin Li 'disable_before_iteration_sysinfo': True, 308*9c5db199SXin Li 'disable_after_test_sysinfo': True, 309*9c5db199SXin Li 'disable_after_iteration_sysinfo': True 310*9c5db199SXin Li }), 311*9c5db199SXin Li CROS_ANDROID_VERSION_PREFIX: 312*9c5db199SXin Li actionables.TestActionable('provision_CheetsUpdate'), 313*9c5db199SXin Li FW_RO_VERSION_PREFIX: 314*9c5db199SXin Li actionables.TestActionable('provision_FirmwareUpdate'), 315*9c5db199SXin Li FW_RW_VERSION_PREFIX: 316*9c5db199SXin Li actionables.TestActionable('provision_FirmwareUpdate', 317*9c5db199SXin Li extra_kwargs={ 318*9c5db199SXin Li 'rw_only': True, 319*9c5db199SXin Li 'tag': 'rw_only' 320*9c5db199SXin Li }), 321*9c5db199SXin Li FW_CR50_RW_VERSION_PREFIX: 322*9c5db199SXin Li actionables.TestActionable('provision_Cr50TOT') 323*9c5db199SXin Li } 324*9c5db199SXin Li 325*9c5db199SXin Li name = 'provision' 326*9c5db199SXin Li 327*9c5db199SXin Li 328*9c5db199SXin Liclass Cleanup(_SpecialTaskAction): 329*9c5db199SXin Li """ 330*9c5db199SXin Li Cleanup runs after a test fails to try and remove artifacts of tests and 331*9c5db199SXin Li ensure the DUT will be in a good state for the next test run. 332*9c5db199SXin Li """ 333*9c5db199SXin Li 334*9c5db199SXin Li _actions = { 335*9c5db199SXin Li 'cleanup-reboot': actionables.RebootActionable(), 336*9c5db199SXin Li } 337*9c5db199SXin Li 338*9c5db199SXin Li name = 'cleanup' 339*9c5db199SXin Li 340*9c5db199SXin Li 341*9c5db199SXin Li# TODO(ayatane): This class doesn't do anything. It's safe to remove 342*9c5db199SXin Li# after all references to it are removed (after some buffer time to be 343*9c5db199SXin Li# safe, like a few release cycles). 344*9c5db199SXin Liclass Repair(_SpecialTaskAction): 345*9c5db199SXin Li """ 346*9c5db199SXin Li Repair runs when one of the other special tasks fails. It should be able 347*9c5db199SXin Li to take a component of the DUT that's in an unknown state and restore it to 348*9c5db199SXin Li a good state. 349*9c5db199SXin Li """ 350*9c5db199SXin Li 351*9c5db199SXin Li _actions = { 352*9c5db199SXin Li } 353*9c5db199SXin Li 354*9c5db199SXin Li name = 'repair' 355*9c5db199SXin Li 356*9c5db199SXin Li 357*9c5db199SXin Li# TODO(milleral): crbug.com/364273 358*9c5db199SXin Li# Label doesn't really mean label in this context. We're putting things into 359*9c5db199SXin Li# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop 360*9c5db199SXin Li# doing that. 361*9c5db199SXin Lidef is_for_special_action(label): 362*9c5db199SXin Li """ 363*9c5db199SXin Li If any special task handles the label specially, then we're using the label 364*9c5db199SXin Li to communicate that we want an action, and not as an actual dependency that 365*9c5db199SXin Li the test has. 366*9c5db199SXin Li 367*9c5db199SXin Li @param label: A string label name. 368*9c5db199SXin Li @return True if any special task handles this label specially, 369*9c5db199SXin Li False if no special task handles this label. 370*9c5db199SXin Li """ 371*9c5db199SXin Li return (Verify.acts_on(label) or 372*9c5db199SXin Li Provision.acts_on(label) or 373*9c5db199SXin Li Cleanup.acts_on(label) or 374*9c5db199SXin Li Repair.acts_on(label) or 375*9c5db199SXin Li label == SKIP_PROVISION) 376*9c5db199SXin Li 377*9c5db199SXin Li 378*9c5db199SXin Lidef join(provision_type, provision_value): 379*9c5db199SXin Li """ 380*9c5db199SXin Li Combine the provision type and value into the label name. 381*9c5db199SXin Li 382*9c5db199SXin Li @param provision_type: One of the constants that are the label prefixes. 383*9c5db199SXin Li @param provision_value: A string of the value for this provision type. 384*9c5db199SXin Li @returns: A string that is the label name for this (type, value) pair. 385*9c5db199SXin Li 386*9c5db199SXin Li >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0') 387*9c5db199SXin Li 'cros-version:lumpy-release/R27-3773.0.0' 388*9c5db199SXin Li 389*9c5db199SXin Li """ 390*9c5db199SXin Li return '%s:%s' % (provision_type, provision_value) 391*9c5db199SXin Li 392*9c5db199SXin Li 393*9c5db199SXin Liclass SpecialTaskActionException(Exception): 394*9c5db199SXin Li """ 395*9c5db199SXin Li Exception raised when a special task fails to successfully run a test that 396*9c5db199SXin Li is required. 397*9c5db199SXin Li 398*9c5db199SXin Li This is also a literally meaningless exception. It's always just discarded. 399*9c5db199SXin Li """ 400