10516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
20516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller# Use of this source code is governed by a BSD-style license that can be
30516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller# found in the LICENSE file.
40516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
50516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
61968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Millerimport abc
70516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
80516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Millerimport common
9af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Dengfrom autotest_lib.server.cros import provision_actionables as actionables
100516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
110516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
120516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller### Constants for label prefixes
130516e4cdc45476e625fd105c43d1fd19f7b395eaAlex MillerCROS_VERSION_PREFIX = 'cros-version'
145ace6f2e17f808efbee863a7d7dbe1bc52527a19Simran BasiANDROID_BUILD_VERSION_PREFIX = 'ab-version'
150723bf5ed7ff506e15fea180547cb6a8ae9102ebDan ShiFW_RW_VERSION_PREFIX = 'fwrw-version'
1636cfd831af781eed114337efa5b90c103a49b502Dan ShiFW_RO_VERSION_PREFIX = 'fwro-version'
170516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
18e92399e32d4e83d7eb01b5c56e53c301a2a6dbc7Chris Sosa# Default number of provisions attempts to try if we believe the devserver is
19e92399e32d4e83d7eb01b5c56e53c301a2a6dbc7Chris Sosa# flaky.
20e92399e32d4e83d7eb01b5c56e53c301a2a6dbc7Chris SosaFLAKY_DEVSERVER_ATTEMPTS = 2
21e92399e32d4e83d7eb01b5c56e53c301a2a6dbc7Chris Sosa
220516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
230516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller### Helpers to convert value to label
240516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Millerdef cros_version_to_label(image):
250516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
260516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    Returns the proper label name for a ChromeOS build of |image|.
270516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
280516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @param image: A string of the form 'lumpy-release/R28-3993.0.0'
290516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @returns: A string that is the appropriate label name.
300516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
310516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
320516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    return CROS_VERSION_PREFIX + ':' + image
330516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
340516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
352d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tamdef fwro_version_to_label(image):
369cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi    """
372d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    Returns the proper label name for a RO firmware build of |image|.
382d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam
392d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    @param image: A string of the form 'lumpy-release/R28-3993.0.0'
402d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    @returns: A string that is the appropriate label name.
412d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam
422d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    """
432d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    return FW_RO_VERSION_PREFIX + ':' + image
442d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam
452d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam
462d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tamdef fwrw_version_to_label(image):
472d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    """
482d00cb2e9ac077edb0d268016653ac2d2e8ead80Tom Wai-Hong Tam    Returns the proper label name for a RW firmware build of |image|.
499cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi
509cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi    @param image: A string of the form 'lumpy-release/R28-3993.0.0'
519cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi    @returns: A string that is the appropriate label name.
529cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi
539cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi    """
540723bf5ed7ff506e15fea180547cb6a8ae9102ebDan Shi    return FW_RW_VERSION_PREFIX + ':' + image
559cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi
569cb0eec5538e2e1e6ca0b7d66daffb7701557cd6Dan Shi
571968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Millerclass _SpecialTaskAction(object):
581968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    """
591968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    Base class to give a template for mapping labels to tests.
601968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    """
611968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
621968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    __metaclass__ = abc.ABCMeta
631968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
641968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
651968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    # One cannot do
661968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    #     @abc.abstractproperty
671968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    #     _actions = {}
681968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    # so this is the next best thing
691968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    @abc.abstractproperty
701968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    def _actions(self):
711968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        """A dictionary mapping labels to test names."""
721968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        pass
731968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
741968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
751968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    @abc.abstractproperty
761968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    def name(self):
771968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        """The name of this special task to be used in output."""
781968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        pass
791968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
801968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
811968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    @classmethod
821968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    def acts_on(cls, label):
831968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        """
841968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        Returns True if the label is a label that we recognize as something we
851968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        know how to act on, given our _actions.
861968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
871968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        @param label: The label as a string.
881968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        @returns: True if there exists a test to run for this label.
891968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
901968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        """
911968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        return label.split(':')[0] in cls._actions
921968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
931968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
941968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    @classmethod
951968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    def test_for(cls, label):
961968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        """
971968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        Returns the test associated with the given (string) label name.
981968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
991968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        @param label: The label for which the action is being requested.
1001968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        @returns: The string name of the test that should be run.
1011968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        @raises KeyError: If the name was not recognized as one we care about.
1021968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1031968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        """
1041968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller        return cls._actions[label]
1050516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1060516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
107aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    @classmethod
108aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    def partition(cls, labels):
109aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        """
110aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        Filter a list of labels into two sets: those labels that we know how to
111aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        act on and those that we don't know how to act on.
112aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller
113aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        @param labels: A list of strings of labels.
114aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        @returns: A tuple where the first element is a set of unactionable
115aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller                  labels, and the second element is a set of the actionable
116aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller                  labels.
117aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        """
118aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        capabilities = set()
119aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        configurations = set()
120aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller
121aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        for label in labels:
122aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller            if cls.acts_on(label):
123aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller                configurations.add(label)
124aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller            else:
125aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller                capabilities.add(label)
126aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller
127aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        return capabilities, configurations
128aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller
129aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller
1301968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Millerclass Verify(_SpecialTaskAction):
1310516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
1321968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    Tests to verify that the DUT is in a sane, known good state that we can run
1331968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    tests on.  Failure to verify leads to running Repair.
1341968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    """
1351968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1361968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    _actions = {
137af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng        'modem_repair': actionables.TestActionable('cellular_StaleModemReboot'),
1386f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # TODO(crbug.com/404421): set rpm action to power_RPMTest after the RPM
1396f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # is stable in lab (destiny). The power_RPMTest failure led to reset job
1406f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # failure and that left dut in Repair Failed. Since the test will fail
1416f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # anyway due to the destiny lab issue, and test retry will retry the
1426f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # test in another DUT.
1436f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # This change temporarily disable the RPM check in reset job.
1446f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # Another way to do this is to remove rpm dependency from tests' control
1456f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # file. That will involve changes on multiple control files. This one
1466f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        # line change here is a simple temporary fix.
1476f7e8005d5f6c4ce6a1d1256013c1e8d0c543c80Don Garrett        'rpm': actionables.TestActionable('dummy_PassServer'),
1481968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    }
1490516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1501968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    name = 'verify'
1510516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1521968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1531968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Millerclass Provision(_SpecialTaskAction):
1541968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    """
1551968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    Provisioning runs to change the configuration of the DUT from one state to
1561968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    another.  It will only be run on verified DUTs.
1570516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
1580516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1591968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    # TODO(milleral): http://crbug.com/249555
1601968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    # Create some way to discover and register provisioning tests so that we
1611968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    # don't need to hand-maintain a list of all of them.
1621968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    _actions = {
1639d548744ea61718c246bd30dffff06f46381981bFang Deng        CROS_VERSION_PREFIX: actionables.TestActionable(
1649d548744ea61718c246bd30dffff06f46381981bFang Deng                'provision_AutoUpdate',
1659d548744ea61718c246bd30dffff06f46381981bFang Deng                extra_kwargs={'disable_sysinfo': False,
1669d548744ea61718c246bd30dffff06f46381981bFang Deng                              'disable_before_test_sysinfo': False,
1679d548744ea61718c246bd30dffff06f46381981bFang Deng                              'disable_before_iteration_sysinfo': True,
1689d548744ea61718c246bd30dffff06f46381981bFang Deng                              'disable_after_test_sysinfo': True,
1699d548744ea61718c246bd30dffff06f46381981bFang Deng                              'disable_after_iteration_sysinfo': True}),
1709a2376144709693922273d777ff7f802055209c5Tom Wai-Hong Tam        FW_RO_VERSION_PREFIX: actionables.TestActionable(
171af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng                'provision_FirmwareUpdate'),
1729a2376144709693922273d777ff7f802055209c5Tom Wai-Hong Tam        FW_RW_VERSION_PREFIX: actionables.TestActionable(
1739a2376144709693922273d777ff7f802055209c5Tom Wai-Hong Tam                'provision_FirmwareUpdate',
1749a2376144709693922273d777ff7f802055209c5Tom Wai-Hong Tam                extra_kwargs={'rw_only': True}),
1755ace6f2e17f808efbee863a7d7dbe1bc52527a19Simran Basi        ANDROID_BUILD_VERSION_PREFIX : actionables.TestActionable(
1765ace6f2e17f808efbee863a7d7dbe1bc52527a19Simran Basi                'provision_AndroidUpdate'),
1771968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    }
1780516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1791968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    name = 'provision'
1801968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1811968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1821968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Millerclass Cleanup(_SpecialTaskAction):
1830516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
1841968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    Cleanup runs after a test fails to try and remove artifacts of tests and
1851968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    ensure the DUT will be in a sane state for the next test run.
1861968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    """
1871968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1881968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    _actions = {
189af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng        'cleanup-reboot': actionables.RebootActionable(),
1901968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    }
1910516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1921968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    name = 'cleanup'
1930516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
1941968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
1951968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Millerclass Repair(_SpecialTaskAction):
1961968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    """
1971968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    Repair runs when one of the other special tasks fails.  It should be able
1981968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    to take a component of the DUT that's in an unknown state and restore it to
1991968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    a good state.
2000516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
2011968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
2021968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    _actions = {
2031968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    }
2041968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
2051968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller    name = 'repair'
2061968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
2071968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
2081968edf2c148ea01f0332613d9eeab383e7d2dd3Alex Miller
209aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller# TODO(milleral): crbug.com/364273
210aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller# Label doesn't really mean label in this context.  We're putting things into
211aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller# DEPENDENCIES that really aren't DEPENDENCIES, and we should probably stop
212aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller# doing that.
213aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Millerdef is_for_special_action(label):
214aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    """
215aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    If any special task handles the label specially, then we're using the label
216aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    to communicate that we want an action, and not as an actual dependency that
217aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    the test has.
218aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller
219aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    @param label: A string label name.
220aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    @return True if any special task handles this label specially,
221aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller            False if no special task handles this label.
222aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    """
223aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    return (Verify.acts_on(label) or
224aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller            Provision.acts_on(label) or
225aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller            Cleanup.acts_on(label) or
226aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller            Repair.acts_on(label))
2270516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2280516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2290516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Millerdef filter_labels(labels):
2300516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
2310516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    Filter a list of labels into two sets: those labels that we know how to
2320516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    change and those that we don't.  For the ones we know how to change, split
2330516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    them apart into the name of configuration type and its value.
2340516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2350516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @param labels: A list of strings of labels.
2360516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @returns: A tuple where the first element is a set of unprovisionable
2370516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller              labels, and the second element is a set of the provisionable
2380516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller              labels.
2390516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2400516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    >>> filter_labels(['bluetooth', 'cros-version:lumpy-release/R28-3993.0.0'])
2410516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    (set(['bluetooth']), set(['cros-version:lumpy-release/R28-3993.0.0']))
2420516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2430516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
244aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller    return Provision.partition(labels)
2450516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2460516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2470516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Millerdef split_labels(labels):
2480516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
2490516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    Split a list of labels into a dict mapping name to value.  All labels must
2500516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    be provisionable labels, or else a ValueError
2510516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2520516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @param labels: list of strings of label names
2530516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @returns: A dict of where the key is the configuration name, and the value
2540516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller              is the configuration value.
2550516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    @raises: ValueError if a label is not a provisionable label.
2560516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2570516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    >>> split_labels(['cros-version:lumpy-release/R28-3993.0.0'])
2580516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    {'cros-version': 'lumpy-release/R28-3993.0.0'}
2590516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    >>> split_labels(['bluetooth'])
2600516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    Traceback (most recent call last):
2610516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    ...
2620516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    ValueError: Unprovisionable label bluetooth
2630516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2640516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    """
2650516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    configurations = dict()
2660516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2670516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    for label in labels:
268aa77200876b530a3b37e6be3ed1e2c0b7098c9dcAlex Miller        if Provision.acts_on(label):
2690516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller            name, value = label.split(':', 1)
2700516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller            configurations[name] = value
2710516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller        else:
2720516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller            raise ValueError('Unprovisionable label %s' % label)
2730516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2740516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller    return configurations
2750516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2760516e4cdc45476e625fd105c43d1fd19f7b395eaAlex Miller
2772229c9c1c5553f781d595d508dcb715050940368Alex Millerdef join(provision_type, provision_value):
2782229c9c1c5553f781d595d508dcb715050940368Alex Miller    """
2792229c9c1c5553f781d595d508dcb715050940368Alex Miller    Combine the provision type and value into the label name.
2802229c9c1c5553f781d595d508dcb715050940368Alex Miller
2812229c9c1c5553f781d595d508dcb715050940368Alex Miller    @param provision_type: One of the constants that are the label prefixes.
2822229c9c1c5553f781d595d508dcb715050940368Alex Miller    @param provision_value: A string of the value for this provision type.
2832229c9c1c5553f781d595d508dcb715050940368Alex Miller    @returns: A string that is the label name for this (type, value) pair.
2842229c9c1c5553f781d595d508dcb715050940368Alex Miller
2852229c9c1c5553f781d595d508dcb715050940368Alex Miller    >>> join(CROS_VERSION_PREFIX, 'lumpy-release/R27-3773.0.0')
2862229c9c1c5553f781d595d508dcb715050940368Alex Miller    'cros-version:lumpy-release/R27-3773.0.0'
2872229c9c1c5553f781d595d508dcb715050940368Alex Miller
2882229c9c1c5553f781d595d508dcb715050940368Alex Miller    """
2892229c9c1c5553f781d595d508dcb715050940368Alex Miller    return '%s:%s' % (provision_type, provision_value)
2902229c9c1c5553f781d595d508dcb715050940368Alex Miller
2912229c9c1c5553f781d595d508dcb715050940368Alex Miller
292667b5f2b48c6096f944a34ca53e769c1031be45fAlex Millerclass SpecialTaskActionException(Exception):
293667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    """
294667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    Exception raised when a special task fails to successfully run a test that
295667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    is required.
296667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
297667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    This is also a literally meaningless exception.  It's always just discarded.
298667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    """
299667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
300667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
301667b5f2b48c6096f944a34ca53e769c1031be45fAlex Millerdef run_special_task_actions(job, host, labels, task):
302667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    """
303667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    Iterate through all `label`s and run any tests on `host` that `task` has
304667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    corresponding to the passed in labels.
305667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
306667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    Emits status lines for each run test, and INFO lines for each skipped label.
307667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
308667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    @param job: A job object from a control file.
309667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    @param host: The host to run actions on.
310667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    @param labels: The list of job labels to work on.
311667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    @param task: An instance of _SpecialTaskAction.
312667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    @returns: None
313667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    @raises: SpecialTaskActionException if a test fails.
314667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
315667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    """
316667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    capabilities, configuration = filter_labels(labels)
317667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
318667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    for label in capabilities:
319667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller        if task.acts_on(label):
320af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng            action_item = task.test_for(label)
321af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng            success = action_item.execute(job=job, host=host)
322667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller            if not success:
323667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller                raise SpecialTaskActionException()
324667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller        else:
325667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller            job.record('INFO', None, task.name,
326667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller                       "Can't %s label '%s'." % (task.name, label))
327667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller
328667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller    for name, value in split_labels(configuration).items():
329667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller        if task.acts_on(name):
330af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng            action_item = task.test_for(name)
331af30e7c634ccbcf14976e397ed372b1ddacee8d7Fang Deng            success = action_item.execute(job=job, host=host, value=value)
332667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller            if not success:
333667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller                raise SpecialTaskActionException()
334667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller        else:
335667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller            job.record('INFO', None, task.name,
336667b5f2b48c6096f944a34ca53e769c1031be45fAlex Miller                       "Can't %s label '%s:%s'." % (task.name, name, value))
337