1a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi# Copyright (c) 2013 The Chromium Authors. All rights reserved.
2a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi# Use of this source code is governed by a BSD-style license that can be
3a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi# found in the LICENSE file.
4a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
5dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
620cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbsimport contextlib
718699fef3341507c6b0d415ced96f6e25e3c03eaFang Dengimport grp
83cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport httplib
93cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport json
10a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shiimport logging
1135d661e09666d315325f8942d06949ca7283666fMK Ryuimport os
12023afc65f9377db51ff6122977a9f529a32422d3beepsimport random
13dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Millerimport re
143cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport time
15ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shiimport traceback
16bef578d9163c6574d626e8b98298c59a5ab79221Paul Drewsimport urllib2
17a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
183cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport common
19ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shifrom autotest_lib.client.bin.result_tools import utils as result_utils
20ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shifrom autotest_lib.client.bin.result_tools import utils_lib as result_utils_lib
21ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shifrom autotest_lib.client.bin.result_tools import view as result_view
22ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shifrom autotest_lib.client.common_lib import utils
23023afc65f9377db51ff6122977a9f529a32422d3beepsfrom autotest_lib.client.common_lib import error
24ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shifrom autotest_lib.client.common_lib import file_utils
25023afc65f9377db51ff6122977a9f529a32422d3beepsfrom autotest_lib.client.common_lib import global_config
260c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryufrom autotest_lib.client.common_lib import host_queue_entry_states
275f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Chengfrom autotest_lib.client.common_lib import host_states
287756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basifrom autotest_lib.server.cros import provision
29a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shifrom autotest_lib.server.cros.dynamic_suite import constants
307e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shifrom autotest_lib.server.cros.dynamic_suite import job_status
31a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
32ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shitry:
33ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    from chromite.lib import metrics
34ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shiexcept ImportError:
35ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    metrics = utils.metrics_mock
36ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
37a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
38d37736b67febeebd991d0209dd030635f7ff5cfdDan ShiCONFIG = global_config.global_config
39d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi
40d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi_SHERIFF_JS = CONFIG.get_config_value('NOTIFICATIONS', 'sheriffs', default='')
41d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi_LAB_SHERIFF_JS = CONFIG.get_config_value(
42d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi        'NOTIFICATIONS', 'lab_sheriffs', default='')
43d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi_CHROMIUM_BUILD_URL = CONFIG.get_config_value(
44d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi        'NOTIFICATIONS', 'chromium_build_url', default='')
45dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
463cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard BarnetteLAB_GOOD_STATES = ('open', 'throttled')
473cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
48d37736b67febeebd991d0209dd030635f7ff5cfdDan ShiENABLE_DRONE_IN_RESTRICTED_SUBNET = CONFIG.get_config_value(
49d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi        'CROS', 'enable_drone_in_restricted_subnet', type=bool,
50d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi        default=False)
513cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
525f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng# Wait at most 10 mins for duts to go idle.
535f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin ChengIDLE_DUT_WAIT_TIMEOUT = 600
545f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
5543274400d21331e2e725bbec429c7f39eaf23364Dan Shi# Mapping between board name and build target. This is for special case handling
5643274400d21331e2e725bbec429c7f39eaf23364Dan Shi# for certain Android board that the board name and build target name does not
5743274400d21331e2e725bbec429c7f39eaf23364Dan Shi# match.
5808fc62ea685fa40c913ed24f8099ba67785bcfd4tturneyANDROID_TARGET_TO_BOARD_MAP = {
5908fc62ea685fa40c913ed24f8099ba67785bcfd4tturney        'seed_l8150': 'gm4g_sprout',
6008fc62ea685fa40c913ed24f8099ba67785bcfd4tturney        'bat_land': 'bat'
6108fc62ea685fa40c913ed24f8099ba67785bcfd4tturney        }
6208fc62ea685fa40c913ed24f8099ba67785bcfd4tturneyANDROID_BOARD_TO_TARGET_MAP = {
6308fc62ea685fa40c913ed24f8099ba67785bcfd4tturney        'gm4g_sprout': 'seed_l8150',
6408fc62ea685fa40c913ed24f8099ba67785bcfd4tturney        'bat': 'bat_land'
6508fc62ea685fa40c913ed24f8099ba67785bcfd4tturney        }
66ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi# Prefix for the metrics name for result size information.
67ffd5b82912ac09842a2b55dd0706343c231bcf3eDan ShiRESULT_METRICS_PREFIX = 'chromeos/autotest/result_collection/'
6843274400d21331e2e725bbec429c7f39eaf23364Dan Shi
69abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnetteclass TestLabException(Exception):
70abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    """Exception raised when the Test Lab blocks a test or suite."""
713cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    pass
723cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
733cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
743cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteclass ParseBuildNameException(Exception):
753cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """Raised when ParseBuildName() cannot parse a build name."""
763cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    pass
773cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
783cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
79f08814a436cdff50ea26571001e72a09e3d57aafFang Dengclass Singleton(type):
80f08814a436cdff50ea26571001e72a09e3d57aafFang Deng    """Enforce that only one client class is instantiated per process."""
81f08814a436cdff50ea26571001e72a09e3d57aafFang Deng    _instances = {}
82f08814a436cdff50ea26571001e72a09e3d57aafFang Deng
83f08814a436cdff50ea26571001e72a09e3d57aafFang Deng    def __call__(cls, *args, **kwargs):
84f08814a436cdff50ea26571001e72a09e3d57aafFang Deng        """Fetch the instance of a class to use for subsequent calls."""
85f08814a436cdff50ea26571001e72a09e3d57aafFang Deng        if cls not in cls._instances:
86f08814a436cdff50ea26571001e72a09e3d57aafFang Deng            cls._instances[cls] = super(Singleton, cls).__call__(
87f08814a436cdff50ea26571001e72a09e3d57aafFang Deng                    *args, **kwargs)
88f08814a436cdff50ea26571001e72a09e3d57aafFang Deng        return cls._instances[cls]
89f08814a436cdff50ea26571001e72a09e3d57aafFang Deng
9005ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Chengclass EmptyAFEHost(object):
9105ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    """Object to represent an AFE host object when there is no AFE."""
9205ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng
9305ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    def __init__(self):
9405ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        """
9505ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        We'll be setting the instance attributes as we use them.  Right now
9605ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        we only use attributes and labels but as time goes by and other
9705ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        attributes are used from an actual AFE Host object (check
9805ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        rpc_interfaces.get_hosts()), we'll add them in here so users won't be
9905ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        perplexed why their host's afe_host object complains that attribute
10005ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        doesn't exist.
10105ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        """
10205ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        self.attributes = {}
10305ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        self.labels = []
10405ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng
105f08814a436cdff50ea26571001e72a09e3d57aafFang Deng
1063cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnettedef ParseBuildName(name):
1073cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """Format a build name, given board, type, milestone, and manifest num.
1083cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
109b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi    @param name: a build name, e.g. 'x86-alex-release/R20-2015.0.0' or a
110b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                 relative build name, e.g. 'x86-alex-release/LATEST'
1113cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
1123cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    @return board: board the manifest is for, e.g. x86-alex.
1133cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    @return type: one of 'release', 'factory', or 'firmware'
1143cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    @return milestone: (numeric) milestone the manifest was associated with.
115b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                        Will be None for relative build names.
116b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi    @return manifest: manifest number, e.g. '2015.0.0'.
117b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                      Will be None for relative build names.
1183cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
1193cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """
120e02810d8efc58845d675176be5ef2356868967a4Dan Shi    match = re.match(r'(trybot-)?(?P<board>[\w-]+?)(?:-chrome)?(?:-chromium)?'
121e02810d8efc58845d675176be5ef2356868967a4Dan Shi                     r'-(?P<type>\w+)/(R(?P<milestone>\d+)-'
122e02810d8efc58845d675176be5ef2356868967a4Dan Shi                     r'(?P<manifest>[\d.ab-]+)|LATEST)',
123f8f648e049ca2f8777fa55c0ea9c4f886efecea9Simran Basi                     name)
124f8f648e049ca2f8777fa55c0ea9c4f886efecea9Simran Basi    if match and len(match.groups()) >= 5:
125b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi        return (match.group('board'), match.group('type'),
126b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                match.group('milestone'), match.group('manifest'))
1273cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    raise ParseBuildNameException('%s is a malformed build name.' % name)
1283cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
129dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
1303d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shidef get_labels_from_afe(hostname, label_prefix, afe):
1313d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    """Retrieve a host's specific labels from the AFE.
1323d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi
1333d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    Looks for the host labels that have the form <label_prefix>:<value>
1343d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    and returns the "<value>" part of the label. None is returned
1353d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    if there is not a label matching the pattern
1363d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi
1373d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    @param hostname: hostname of given DUT.
1383d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    @param label_prefix: prefix of label to be matched, e.g., |board:|
1393d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    @param afe: afe instance.
1403d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi
1413d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    @returns A list of labels that match the prefix or 'None'
1423d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi
1433d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    """
1443d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    labels = afe.get_labels(name__startswith=label_prefix,
1453d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi                            host__hostname__in=[hostname])
1463d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    if labels:
1473d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi        return [l.name.split(label_prefix, 1)[1] for l in labels]
1483d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi
1493d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi
150a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shidef get_label_from_afe(hostname, label_prefix, afe):
151a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """Retrieve a host's specific label from the AFE.
152a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
153a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    Looks for a host label that has the form <label_prefix>:<value>
154a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    and returns the "<value>" part of the label. None is returned
155a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    if there is not a label matching the pattern
156a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
157a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param hostname: hostname of given DUT.
158a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param label_prefix: prefix of label to be matched, e.g., |board:|
159a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param afe: afe instance.
160a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @returns the label that matches the prefix or 'None'
161a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
162a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """
1633d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi    labels = get_labels_from_afe(hostname, label_prefix, afe)
164a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    if labels and len(labels) == 1:
1653d7a0e14e220d7ea2cbe00013959c6d967a6a41aDan Shi        return labels[0]
166a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
167a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
168a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shidef get_board_from_afe(hostname, afe):
169a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """Retrieve given host's board from its labels in the AFE.
170a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
171a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    Looks for a host label of the form "board:<board>", and
172a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    returns the "<board>" part of the label.  `None` is returned
173a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    if there is not a single, unique label matching the pattern.
174a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
175a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param hostname: hostname of given DUT.
176a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param afe: afe instance.
177a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @returns board from label, or `None`.
178a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
179a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """
180a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    return get_label_from_afe(hostname, constants.BOARD_PREFIX, afe)
181a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
182a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
183a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shidef get_build_from_afe(hostname, afe):
184a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """Retrieve the current build for given host from the AFE.
185a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
186a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    Looks through the host's labels in the AFE to determine its build.
187a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
188a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param hostname: hostname of given DUT.
189a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param afe: afe instance.
190a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @returns The current build or None if it could not find it or if there
191a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi             were multiple build labels assigned to this host.
192a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
193a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """
1947756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basi    for prefix in [provision.CROS_VERSION_PREFIX,
1957756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basi                   provision.ANDROID_BUILD_VERSION_PREFIX]:
1967756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basi        build = get_label_from_afe(hostname, prefix + ':', afe)
1977756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basi        if build:
1987756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basi            return build
1997756a0b6949729cb5a632955ebd8fe5dbd308c08Simran Basi    return None
200a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
201a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
2026a61239a702907ad1aedd56aa4ee700ca50bbab0Allen Li# TODO(fdeng): fix get_sheriffs crbug.com/483254
2033197b39f82eb92afff33c7d44b805afe120c7627Fang Dengdef get_sheriffs(lab_only=False):
204dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    """
205dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    Polls the javascript file that holds the identity of the sheriff and
206dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    parses it's output to return a list of chromium sheriff email addresses.
207dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    The javascript file can contain the ldap of more than one sheriff, eg:
208dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    document.write('sheriff_one, sheriff_two').
209dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
2103197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    @param lab_only: if True, only pulls lab sheriff.
2113197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    @return: A list of chroium.org sheriff email addresses to cc on the bug.
2123197b39f82eb92afff33c7d44b805afe120c7627Fang Deng             An empty list if failed to parse the javascript.
213dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    """
214dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    sheriff_ids = []
2153197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    sheriff_js_list = _LAB_SHERIFF_JS.split(',')
2163197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    if not lab_only:
2173197b39f82eb92afff33c7d44b805afe120c7627Fang Deng        sheriff_js_list.extend(_SHERIFF_JS.split(','))
2183197b39f82eb92afff33c7d44b805afe120c7627Fang Deng
2193197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    for sheriff_js in sheriff_js_list:
220dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller        try:
221ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi            url_content = utils.urlopen('%s%s'% (
222dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller                _CHROMIUM_BUILD_URL, sheriff_js)).read()
223dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller        except (ValueError, IOError) as e:
2244efdf03d1057f9d38e043b7c2affe842109805d2beeps            logging.warning('could not parse sheriff from url %s%s: %s',
2254efdf03d1057f9d38e043b7c2affe842109805d2beeps                             _CHROMIUM_BUILD_URL, sheriff_js, str(e))
226bef578d9163c6574d626e8b98298c59a5ab79221Paul Drews        except (urllib2.URLError, httplib.HTTPException) as e:
227bef578d9163c6574d626e8b98298c59a5ab79221Paul Drews            logging.warning('unexpected error reading from url "%s%s": %s',
228bef578d9163c6574d626e8b98298c59a5ab79221Paul Drews                             _CHROMIUM_BUILD_URL, sheriff_js, str(e))
229dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller        else:
230dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            ldaps = re.search(r"document.write\('(.*)'\)", url_content)
231dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            if not ldaps:
2324efdf03d1057f9d38e043b7c2affe842109805d2beeps                logging.warning('Could not retrieve sheriff ldaps for: %s',
2334efdf03d1057f9d38e043b7c2affe842109805d2beeps                                 url_content)
234dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller                continue
235dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            sheriff_ids += ['%s@chromium.org' % alias.replace(' ', '')
236dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller                            for alias in ldaps.group(1).split(',')]
237dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    return sheriff_ids
23846dadc9439355f72d394dcc4700902001bd797ffbeeps
23946dadc9439355f72d394dcc4700902001bd797ffbeeps
24046dadc9439355f72d394dcc4700902001bd797ffbeepsdef remote_wget(source_url, dest_path, ssh_cmd):
24146dadc9439355f72d394dcc4700902001bd797ffbeeps    """wget source_url from localhost to dest_path on remote host using ssh.
24246dadc9439355f72d394dcc4700902001bd797ffbeeps
24346dadc9439355f72d394dcc4700902001bd797ffbeeps    @param source_url: The complete url of the source of the package to send.
24446dadc9439355f72d394dcc4700902001bd797ffbeeps    @param dest_path: The path on the remote host's file system where we would
24546dadc9439355f72d394dcc4700902001bd797ffbeeps        like to store the package.
24646dadc9439355f72d394dcc4700902001bd797ffbeeps    @param ssh_cmd: The ssh command to use in performing the remote wget.
24746dadc9439355f72d394dcc4700902001bd797ffbeeps    """
24846dadc9439355f72d394dcc4700902001bd797ffbeeps    wget_cmd = ("wget -O - %s | %s 'cat >%s'" %
24946dadc9439355f72d394dcc4700902001bd797ffbeeps                (source_url, ssh_cmd, dest_path))
250ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi    utils.run(wget_cmd)
25146dadc9439355f72d394dcc4700902001bd797ffbeeps
2523cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
253266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette_MAX_LAB_STATUS_ATTEMPTS = 5
254266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnettedef _get_lab_status(status_url):
2553cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """Grabs the current lab status and message.
2563cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
257266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    @returns The JSON object obtained from the given URL.
258266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
2593cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """
2603cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    retry_waittime = 1
261266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    for _ in range(_MAX_LAB_STATUS_ATTEMPTS):
2623cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        try:
2633cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette            response = urllib2.urlopen(status_url)
2643cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        except IOError as e:
265266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette            logging.debug('Error occurred when grabbing the lab status: %s.',
2663cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette                          e)
2673cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette            time.sleep(retry_waittime)
2683cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette            continue
2693cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        # Check for successful response code.
2703cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        if response.getcode() == 200:
271266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette            return json.load(response)
2723cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        time.sleep(retry_waittime)
273266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    return None
2743cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
2753cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
276abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnettedef _decode_lab_status(lab_status, build):
277266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    """Decode lab status, and report exceptions as needed.
2783cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
279abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    Take a deserialized JSON object from the lab status page, and
280abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    interpret it to determine the actual lab status.  Raise
281266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    exceptions as required to report when the lab is down.
2823cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
283abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @param build: build name that we want to check the status of.
2843cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
285abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @raises TestLabException Raised if a request to test for the given
286abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                             status and build should be blocked.
2873cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """
2883cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    # First check if the lab is up.
289266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    if not lab_status['general_state'] in LAB_GOOD_STATES:
290abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette        raise TestLabException('Chromium OS Test Lab is closed: '
291abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                               '%s.' % lab_status['message'])
2923cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
293abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    # Check if the build we wish to use is disabled.
2943cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    # Lab messages should be in the format of:
295abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    #    Lab is 'status' [regex ...] (comment)
296abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    # If the build name matches any regex, it will be blocked.
297abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    build_exceptions = re.search('\[(.*)\]', lab_status['message'])
298ae43721d4a11c6ecd2d502bba73b185b05bfce29Prashanth Balasubramanian    if not build_exceptions or not build:
299abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette        return
300abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    for build_pattern in build_exceptions.group(1).split():
3017f215d3906ac204694d64b7eaa85155777f88f39J. Richard Barnette        if re.match(build_pattern, build):
302abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette            raise TestLabException('Chromium OS Test Lab is closed: '
303abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                                   '%s matches %s.' % (
304abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                                           build, build_pattern))
3053cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    return
306266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
307266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
30894234cb747dc067b159789a4b63f21b23d706922Dan Shidef is_in_lab():
30994234cb747dc067b159789a4b63f21b23d706922Dan Shi    """Check if current Autotest instance is in lab
31094234cb747dc067b159789a4b63f21b23d706922Dan Shi
31194234cb747dc067b159789a4b63f21b23d706922Dan Shi    @return: True if the Autotest instance is in lab.
31294234cb747dc067b159789a4b63f21b23d706922Dan Shi    """
313d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi    test_server_name = CONFIG.get_config_value('SERVER', 'hostname')
31494234cb747dc067b159789a4b63f21b23d706922Dan Shi    return test_server_name.startswith('cautotest')
31594234cb747dc067b159789a4b63f21b23d706922Dan Shi
31694234cb747dc067b159789a4b63f21b23d706922Dan Shi
317abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnettedef check_lab_status(build):
318abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    """Check if the lab status allows us to schedule for a build.
319266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
320abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    Checks if the lab is down, or if testing for the requested build
321abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    should be blocked.
322266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
323abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @param build: Name of the build to be scheduled for testing.
324266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
325abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @raises TestLabException Raised if a request to test for the given
326abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                             status and build should be blocked.
327266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
328266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    """
329266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    # Ensure we are trying to schedule on the actual lab.
33094234cb747dc067b159789a4b63f21b23d706922Dan Shi    if not is_in_lab():
331266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette        return
332266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
333266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    # Download the lab status from its home on the web.
334d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi    status_url = CONFIG.get_config_value('CROS', 'lab_status_url')
335266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    json_status = _get_lab_status(status_url)
336266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    if json_status is None:
337266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette        # We go ahead and say the lab is open if we can't get the status.
33804be2bd5e4666a5c253e9c30ab20555e04286032Ilja H. Friedel        logging.warning('Could not get a status from %s', status_url)
339266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette        return
340abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    _decode_lab_status(json_status, build)
341023afc65f9377db51ff6122977a9f529a32422d3beeps
342023afc65f9377db51ff6122977a9f529a32422d3beeps
343023afc65f9377db51ff6122977a9f529a32422d3beepsdef lock_host_with_labels(afe, lock_manager, labels):
344023afc65f9377db51ff6122977a9f529a32422d3beeps    """Lookup and lock one host that matches the list of input labels.
345023afc65f9377db51ff6122977a9f529a32422d3beeps
346023afc65f9377db51ff6122977a9f529a32422d3beeps    @param afe: An instance of the afe class, as defined in server.frontend.
347023afc65f9377db51ff6122977a9f529a32422d3beeps    @param lock_manager: A lock manager capable of locking hosts, eg the
348023afc65f9377db51ff6122977a9f529a32422d3beeps        one defined in server.cros.host_lock_manager.
349023afc65f9377db51ff6122977a9f529a32422d3beeps    @param labels: A list of labels to look for on hosts.
350023afc65f9377db51ff6122977a9f529a32422d3beeps
351023afc65f9377db51ff6122977a9f529a32422d3beeps    @return: The hostname of a host matching all labels, and locked through the
352023afc65f9377db51ff6122977a9f529a32422d3beeps        lock_manager. The hostname will be as specified in the database the afe
353023afc65f9377db51ff6122977a9f529a32422d3beeps        object is associated with, i.e if it exists in afe_hosts with a .cros
354023afc65f9377db51ff6122977a9f529a32422d3beeps        suffix, the hostname returned will contain a .cros suffix.
355023afc65f9377db51ff6122977a9f529a32422d3beeps
356023afc65f9377db51ff6122977a9f529a32422d3beeps    @raises: error.NoEligibleHostException: If no hosts matching the list of
357023afc65f9377db51ff6122977a9f529a32422d3beeps        input labels are available.
358023afc65f9377db51ff6122977a9f529a32422d3beeps    @raises: error.TestError: If unable to lock a host matching the labels.
359023afc65f9377db51ff6122977a9f529a32422d3beeps    """
360023afc65f9377db51ff6122977a9f529a32422d3beeps    potential_hosts = afe.get_hosts(multiple_labels=labels)
361023afc65f9377db51ff6122977a9f529a32422d3beeps    if not potential_hosts:
362023afc65f9377db51ff6122977a9f529a32422d3beeps        raise error.NoEligibleHostException(
363023afc65f9377db51ff6122977a9f529a32422d3beeps                'No devices found with labels %s.' % labels)
364023afc65f9377db51ff6122977a9f529a32422d3beeps
365023afc65f9377db51ff6122977a9f529a32422d3beeps    # This prevents errors where a fault might seem repeatable
366023afc65f9377db51ff6122977a9f529a32422d3beeps    # because we lock, say, the same packet capturer for each test run.
367023afc65f9377db51ff6122977a9f529a32422d3beeps    random.shuffle(potential_hosts)
368023afc65f9377db51ff6122977a9f529a32422d3beeps    for host in potential_hosts:
369023afc65f9377db51ff6122977a9f529a32422d3beeps        if lock_manager.lock([host.hostname]):
370023afc65f9377db51ff6122977a9f529a32422d3beeps            logging.info('Locked device %s with labels %s.',
371023afc65f9377db51ff6122977a9f529a32422d3beeps                         host.hostname, labels)
372023afc65f9377db51ff6122977a9f529a32422d3beeps            return host.hostname
373023afc65f9377db51ff6122977a9f529a32422d3beeps        else:
374023afc65f9377db51ff6122977a9f529a32422d3beeps            logging.info('Unable to lock device %s with labels %s.',
375023afc65f9377db51ff6122977a9f529a32422d3beeps                         host.hostname, labels)
376023afc65f9377db51ff6122977a9f529a32422d3beeps
377023afc65f9377db51ff6122977a9f529a32422d3beeps    raise error.TestError('Could not lock a device with labels %s' % labels)
3787e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3797e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3807e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shidef get_test_views_from_tko(suite_job_id, tko):
3817e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    """Get test name and result for given suite job ID.
3827e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3837e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @param suite_job_id: ID of suite job.
3847e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @param tko: an instance of TKO as defined in server/frontend.py.
3857e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @return: A dictionary of test status keyed by test name, e.g.,
3867e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi             {'dummy_Fail.Error': 'ERROR', 'dummy_Fail.NAError': 'TEST_NA'}
3877e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @raise: Exception when there is no test view found.
3887e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3897e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    """
3907e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    views = tko.run('get_detailed_test_views', afe_job_id=suite_job_id)
3917e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    relevant_views = filter(job_status.view_is_relevant, views)
3927e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    if not relevant_views:
3937e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi        raise Exception('Failed to retrieve job results.')
3947e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3957e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    test_views = {}
3967e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    for view in relevant_views:
3977e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi        test_views[view['test_name']] = view['status']
3987e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3997e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    return test_views
40035d661e09666d315325f8942d06949ca7283666fMK Ryu
40135d661e09666d315325f8942d06949ca7283666fMK Ryu
402c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryudef get_data_key(prefix, suite, build, board):
403c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    """
404c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    Constructs a key string from parameters.
405c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu
406c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param prefix: Prefix for the generating key.
407c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param suite: a suite name. e.g., bvt-cq, bvt-inline, dummy
408c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param build: The build string. This string should have a consistent
409c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        format eg: x86-mario-release/R26-3570.0.0. If the format of this
410c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        string changes such that we can't determine build_type or branch
411c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        we give up and use the parametes we're sure of instead (suite,
412c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        board). eg:
413c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            1. build = x86-alex-pgo-release/R26-3570.0.0
414c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               branch = 26
415c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               build_type = pgo-release
416c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            2. build = lumpy-paladin/R28-3993.0.0-rc5
417c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               branch = 28
418c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               build_type = paladin
419c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param board: The board that this suite ran on.
420c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @return: The key string used for a dictionary.
421c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    """
422c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    try:
423c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        _board, build_type, branch = ParseBuildName(build)[:3]
424c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    except ParseBuildNameException as e:
425c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        logging.error(str(e))
426c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        branch = 'Unknown'
427c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        build_type = 'Unknown'
428c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    else:
429c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        embedded_str = re.search(r'x86-\w+-(.*)', _board)
430c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        if embedded_str:
431c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            build_type = embedded_str.group(1) + '-' + build_type
432c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu
433c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    data_key_dict = {
434c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'prefix': prefix,
435c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'board': board,
436c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'branch': branch,
437c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'build_type': build_type,
438c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'suite': suite,
439c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    }
440c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    return ('%(prefix)s.%(board)s.%(build_type)s.%(branch)s.%(suite)s'
441c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            % data_key_dict)
44283184356b60f4352e46e69488d54222032d426c0MK Ryu
44383184356b60f4352e46e69488d54222032d426c0MK Ryu
4442d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryudef setup_logging(logfile=None, prefix=False):
44583184356b60f4352e46e69488d54222032d426c0MK Ryu    """Setup basic logging with all logging info stripped.
44683184356b60f4352e46e69488d54222032d426c0MK Ryu
44783184356b60f4352e46e69488d54222032d426c0MK Ryu    Calls to logging will only show the message. No severity is logged.
44883184356b60f4352e46e69488d54222032d426c0MK Ryu
44983184356b60f4352e46e69488d54222032d426c0MK Ryu    @param logfile: If specified dump output to a file as well.
4502d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    @param prefix: Flag for log prefix. Set to True to add prefix to log
4512d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        entries to include timestamp and log level. Default is False.
45283184356b60f4352e46e69488d54222032d426c0MK Ryu    """
45383184356b60f4352e46e69488d54222032d426c0MK Ryu    # Remove all existing handlers. client/common_lib/logging_config adds
45483184356b60f4352e46e69488d54222032d426c0MK Ryu    # a StreamHandler to logger when modules are imported, e.g.,
45583184356b60f4352e46e69488d54222032d426c0MK Ryu    # autotest_lib.client.bin.utils. A new StreamHandler will be added here to
45683184356b60f4352e46e69488d54222032d426c0MK Ryu    # log only messages, not severity.
45783184356b60f4352e46e69488d54222032d426c0MK Ryu    logging.getLogger().handlers = []
45883184356b60f4352e46e69488d54222032d426c0MK Ryu
4592d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    if prefix:
4602d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        log_format = '%(asctime)s %(levelname)-5s| %(message)s'
4612d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    else:
4622d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        log_format = '%(message)s'
4632d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu
46483184356b60f4352e46e69488d54222032d426c0MK Ryu    screen_handler = logging.StreamHandler()
4652d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    screen_handler.setFormatter(logging.Formatter(log_format))
46683184356b60f4352e46e69488d54222032d426c0MK Ryu    logging.getLogger().addHandler(screen_handler)
46783184356b60f4352e46e69488d54222032d426c0MK Ryu    logging.getLogger().setLevel(logging.INFO)
46883184356b60f4352e46e69488d54222032d426c0MK Ryu    if logfile:
46983184356b60f4352e46e69488d54222032d426c0MK Ryu        file_handler = logging.FileHandler(logfile)
4702d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        file_handler.setFormatter(logging.Formatter(log_format))
47183184356b60f4352e46e69488d54222032d426c0MK Ryu        file_handler.setLevel(logging.DEBUG)
47283184356b60f4352e46e69488d54222032d426c0MK Ryu        logging.getLogger().addHandler(file_handler)
4738c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4748c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4758c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramaniandef is_shard():
4768c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    """Determines if this instance is running as a shard.
4778c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4788c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    Reads the global_config value shard_hostname in the section SHARD.
4798c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4808c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    @return True, if shard_hostname is set, False otherwise.
4818c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    """
482d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi    hostname = CONFIG.get_config_value('SHARD', 'shard_hostname', default=None)
4830c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return bool(hostname)
4840c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4850c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4860cb2a3b1d2d86d70da06a3f45be9297139e48207Fang Dengdef get_global_afe_hostname():
4870cb2a3b1d2d86d70da06a3f45be9297139e48207Fang Deng    """Read the hostname of the global AFE from the global configuration."""
488d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi    return CONFIG.get_config_value('SERVER', 'global_afe_hostname')
4890cb2a3b1d2d86d70da06a3f45be9297139e48207Fang Deng
4900cb2a3b1d2d86d70da06a3f45be9297139e48207Fang Deng
49118699fef3341507c6b0d415ced96f6e25e3c03eaFang Dengdef is_restricted_user(username):
49218699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    """Determines if a user is in a restricted group.
49318699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng
49418699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    User in restricted group only have access to master.
49518699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng
49618699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    @param username: A string, representing a username.
49718699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng
49818699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    @returns: True if the user is in a restricted group.
49918699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    """
50018699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    if not username:
50118699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng        return False
50218699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng
503d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi    restricted_groups = CONFIG.get_config_value(
50418699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng            'AUTOTEST_WEB', 'restricted_groups', default='').split(',')
50518699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    for group in restricted_groups:
5065229c85219e09d6a7857701be20f286ba04809b3Fang Deng        try:
5075229c85219e09d6a7857701be20f286ba04809b3Fang Deng            if group and username in grp.getgrnam(group).gr_mem:
5085229c85219e09d6a7857701be20f286ba04809b3Fang Deng                return True
5095229c85219e09d6a7857701be20f286ba04809b3Fang Deng        except KeyError as e:
5105229c85219e09d6a7857701be20f286ba04809b3Fang Deng            logging.debug("%s is not a valid group.", group)
51118699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng    return False
51218699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng
51318699fef3341507c6b0d415ced96f6e25e3c03eaFang Deng
5140c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_special_task_status(is_complete, success, is_active):
5150c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Get the status of a special task.
5160c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5170c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    Emulate a host queue entry status for a special task
5180c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    Although SpecialTasks are not HostQueueEntries, it is helpful to
5190c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    the user to present similar statuses.
5200c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5210c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param is_complete    Boolean if the task is completed.
5220c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param success        Boolean if the task succeeded.
5230c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param is_active      Boolean if the task is active.
5240c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5250c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @return The status of a special task.
5260c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
5270c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    if is_complete:
5280c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        if success:
5290c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu            return host_queue_entry_states.Status.COMPLETED
5300c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        return host_queue_entry_states.Status.FAILED
5310c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    if is_active:
5320c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        return host_queue_entry_states.Status.RUNNING
5330c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return host_queue_entry_states.Status.QUEUED
5340c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5350c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5360c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_special_task_exec_path(hostname, task_id, task_name, time_requested):
5370c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Get the execution path of the SpecialTask.
5380c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5390c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    This method returns different paths depending on where a
5400c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    the task ran:
5410c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        * Master: hosts/hostname/task_id-task_type
5420c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        * Shard: Master_path/time_created
5430c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    This is to work around the fact that a shard can fail independent
5440c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    of the master, and be replaced by another shard that has the same
5450c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    hosts. Without the time_created stamp the logs of the tasks running
5460c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    on the second shard will clobber the logs from the first in google
5470c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    storage, because task ids are not globally unique.
5480c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5490c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param hostname        Hostname
5500c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param task_id         Special task id
5510c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param task_name       Special task name (e.g., Verify, Repair, etc)
5520c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param time_requested  Special task requested time.
5530c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5540c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @return An execution path for the task.
5550c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
5560c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    results_path = 'hosts/%s/%s-%s' % (hostname, task_id, task_name.lower())
5570c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5580c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # If we do this on the master it will break backward compatibility,
5590c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # as there are tasks that currently don't have timestamps. If a host
5600c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # or job has been sent to a shard, the rpc for that host/job will
5610c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # be redirected to the shard, so this global_config check will happen
5620c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # on the shard the logs are on.
5630c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    if not is_shard():
5640c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        return results_path
5650c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5660c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # Generate a uid to disambiguate special task result directories
5670c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # in case this shard fails. The simplest uid is the job_id, however
5680c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # in rare cases tasks do not have jobs associated with them (eg:
5690c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # frontend verify), so just use the creation timestamp. The clocks
5700c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # between a shard and master should always be in sync. Any discrepancies
5710c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # will be brought to our attention in the form of job timeouts.
5720c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    uid = time_requested.strftime('%Y%d%m%H%M%S')
5730c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5740c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # TODO: This is a hack, however it is the easiest way to achieve
5750c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # correctness. There is currently some debate over the future of
5760c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # tasks in our infrastructure and refactoring everything right
5770c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # now isn't worth the time.
5780c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return '%s/%s' % (results_path, uid)
5790c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5800c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5810c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_job_tag(id, owner):
5820c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Returns a string tag for a job.
5830c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5840c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param id    Job id
5850c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param owner Job owner
5860c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5870c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
5880c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return '%s-%s' % (id, owner)
5890c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5900c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5910c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_hqe_exec_path(tag, execution_subdir):
5920c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Returns a execution path to a HQE's results.
5930c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5940c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param tag               Tag string for a job associated with a HQE.
5950c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param execution_subdir  Execution sub-directory string of a HQE.
5960c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5970c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
5980c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return os.path.join(tag, execution_subdir)
59982997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
60082997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
60182997b9a9791105d81c9db14e75ed14946f78f94Dan Shidef is_inside_chroot():
60282997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    """Check if the process is running inside chroot.
60382997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
60482997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    This is a wrapper around chromite.lib.cros_build_lib.IsInsideChroot(). The
60582997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    method checks if cros_build_lib can be imported first.
60682997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
60782997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    @return: True if the process is running inside chroot or cros_build_lib
60882997b9a9791105d81c9db14e75ed14946f78f94Dan Shi             cannot be imported.
60982997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
61082997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    """
61116b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    try:
61216b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # TODO(crbug.com/739466) This module import is delayed because it adds
61316b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # 1-2 seconds to the module import time and most users of site_utils
61416b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # don't need it. The correct fix is to break apart site_utils into more
61516b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # meaningful chunks.
61616b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        from chromite.lib import cros_build_lib
61716b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    except ImportError:
61816b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        logging.warn('Unable to import chromite. Can not detect chroot. '
61916b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu                     'Defaulting to False')
62016b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        return False
62116b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    return cros_build_lib.IsInsideChroot()
62270647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
62370647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
62470647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shidef parse_job_name(name):
62570647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    """Parse job name to get information including build, board and suite etc.
62670647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
62770647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    Suite job created by run_suite follows the naming convention of:
62870647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    [build]-test_suites/control.[suite]
62970647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    For example: lumpy-release/R46-7272.0.0-test_suites/control.bvt
630cdd00f20ac7607ff89a97e83e2483a4c8feddb7bAllen Li    The naming convention is defined in rpc_interface.create_suite_job.
63170647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
63270647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    Test job created by suite job follows the naming convention of:
63370647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    [build]/[suite]/[test name]
63470647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    For example: lumpy-release/R46-7272.0.0/bvt/login_LoginSuccess
63570647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    The naming convention is defined in
63670647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    server/cros/dynamic_suite/tools.create_job_name
63770647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
63870647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    Note that pgo and chrome-perf builds will fail the method. Since lab does
63970647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    not run test for these builds, they can be ignored.
640ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi    Also, tests for Launch Control builds have different naming convention.
641ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi    The build ID will be used as build_version.
64270647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
64370647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    @param name: Name of the job.
64470647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
64570647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    @return: A dictionary containing the test information. The keyvals include:
64670647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi             build: Name of the build, e.g., lumpy-release/R46-7272.0.0
64770647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi             build_version: The version of the build, e.g., R46-7272.0.0
64870647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi             board: Name of the board, e.g., lumpy
64970647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi             suite: Name of the test suite, e.g., bvt
65070647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi
65170647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    """
65270647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    info = {}
653ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi    suite_job_regex = '([^/]*/[^/]*(?:/\d+)?)-test_suites/control\.(.*)'
654ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi    test_job_regex = '([^/]*/[^/]*(?:/\d+)?)/([^/]+)/.*'
65570647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    match = re.match(suite_job_regex, name)
65670647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    if not match:
65770647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi        match = re.match(test_job_regex, name)
65870647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    if match:
65970647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi        info['build'] = match.groups()[0]
66070647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi        info['suite'] = match.groups()[1]
66170647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi        info['build_version'] = info['build'].split('/')[1]
66270647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi        try:
66370647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi            info['board'], _, _, _ = ParseBuildName(info['build'])
66470647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi        except ParseBuildNameException:
665ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi            # Try to parse it as Launch Control build
666ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi            # Launch Control builds have name format:
667ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi            # branch/build_target-build_type/build_id.
668ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi            try:
669ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                _, target, build_id = utils.parse_launch_control_build(
670ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                        info['build'])
671ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                build_target, _ = utils.parse_launch_control_target(target)
672ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                if build_target:
673ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                    info['board'] = build_target
674ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                    info['build_version'] = build_id
675ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi            except ValueError:
676ef31f03d7347b40c431754a6280b5ff9eea2903fDan Shi                pass
67770647cafbd061a7754ac304fd9dc067f2b6dbab4Dan Shi    return info
6783a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng
6793a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng
6803a4a57a7704ab15136286628f465dbb2d1a56171Kevin Chengdef add_label_detector(label_function_list, label_list=None, label=None):
6813a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    """Decorator used to group functions together into the provided list.
6823a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng
6833a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    This is a helper function to automatically add label functions that have
6843a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    the label decorator.  This is to help populate the class list of label
6853a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    functions to be retrieved by the get_labels class method.
6863a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng
6873a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    @param label_function_list: List of label detecting functions to add
6883a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng                                decorated function to.
6893a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    @param label_list: List of detectable labels to add detectable labels to.
6903a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng                       (Default: None)
6913a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    @param label: Label string that is detectable by this detection function
6923a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng                  (Default: None)
6933a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    """
6943a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    def add_func(func):
6953a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng        """
6963a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng        @param func: The function to be added as a detector.
6973a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng        """
6983a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng        label_function_list.append(func)
6993a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng        if label and label_list is not None:
7003a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng            label_list.append(label)
7013a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng        return func
7023a4a57a7704ab15136286628f465dbb2d1a56171Kevin Cheng    return add_func
7039f364a6fe1fedaecc652f111cd143ab2ee7ca825Simran Basi
7049f364a6fe1fedaecc652f111cd143ab2ee7ca825Simran Basi
7059f364a6fe1fedaecc652f111cd143ab2ee7ca825Simran Basidef verify_not_root_user():
7069f364a6fe1fedaecc652f111cd143ab2ee7ca825Simran Basi    """Simple function to error out if running with uid == 0"""
7079f364a6fe1fedaecc652f111cd143ab2ee7ca825Simran Basi    if os.getuid() == 0:
7081bf60eb788365f083d0ee8045a6556f906149decSimran Basi        raise error.IllegalUser('This script can not be ran as root.')
7091bf60eb788365f083d0ee8045a6556f906149decSimran Basi
7101bf60eb788365f083d0ee8045a6556f906149decSimran Basi
7111bf60eb788365f083d0ee8045a6556f906149decSimran Basidef get_hostname_from_machine(machine):
7121bf60eb788365f083d0ee8045a6556f906149decSimran Basi    """Lookup hostname from a machine string or dict.
7131bf60eb788365f083d0ee8045a6556f906149decSimran Basi
7141bf60eb788365f083d0ee8045a6556f906149decSimran Basi    @returns: Machine hostname in string format.
7151bf60eb788365f083d0ee8045a6556f906149decSimran Basi    """
7161bf60eb788365f083d0ee8045a6556f906149decSimran Basi    hostname, _ = get_host_info_from_machine(machine)
7171bf60eb788365f083d0ee8045a6556f906149decSimran Basi    return hostname
7181bf60eb788365f083d0ee8045a6556f906149decSimran Basi
7191bf60eb788365f083d0ee8045a6556f906149decSimran Basi
7201bf60eb788365f083d0ee8045a6556f906149decSimran Basidef get_host_info_from_machine(machine):
7211bf60eb788365f083d0ee8045a6556f906149decSimran Basi    """Lookup host information from a machine string or dict.
7221bf60eb788365f083d0ee8045a6556f906149decSimran Basi
72305ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    @returns: Tuple of (hostname, afe_host)
7241bf60eb788365f083d0ee8045a6556f906149decSimran Basi    """
7251bf60eb788365f083d0ee8045a6556f906149decSimran Basi    if isinstance(machine, dict):
72605ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        return (machine['hostname'], machine['afe_host'])
7271bf60eb788365f083d0ee8045a6556f906149decSimran Basi    else:
72805ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng        return (machine, EmptyAFEHost())
72905ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng
73005ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng
73105ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Chengdef get_afe_host_from_machine(machine):
73205ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    """Return the afe_host from the machine dict if possible.
73305ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng
73405ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    @returns: AFE host object.
73505ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    """
73605ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    _, afe_host = get_host_info_from_machine(machine)
73705ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    return afe_host
738f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng
739f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng
74006893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abedef get_connection_pool_from_machine(machine):
74106893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe    """Returns the ssh_multiplex.ConnectionPool from machine if possible."""
74206893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe    if not isinstance(machine, dict):
74306893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe        return None
74406893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe    return machine.get('connection_pool')
74506893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe
74606893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe
747f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Dengdef get_creds_abspath(creds_file):
748f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    """Returns the abspath of the credentials file.
749f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng
750f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    If creds_file is already an absolute path, just return it.
751f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    Otherwise, assume it is located in the creds directory
752f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    specified in global_config and return the absolute path.
753f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng
754f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    @param: creds_path, a path to the credentials.
755f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    @return: An absolute path to the credentials file.
756f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    """
757f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    if not creds_file:
758f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng        return None
759f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    if os.path.isabs(creds_file):
760f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng        return creds_file
761d37736b67febeebd991d0209dd030635f7ff5cfdDan Shi    creds_dir = CONFIG.get_config_value('SERVER', 'creds_dir', default='')
762f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    if not creds_dir or not os.path.exists(creds_dir):
763f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng        creds_dir = common.autotest_dir
764f8a94e2adf24e017be4e9d0687d4ab8f46ac4fceFang Deng    return os.path.join(creds_dir, creds_file)
7653b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng
7663b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng
7673b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Chengdef machine_is_testbed(machine):
7683b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng    """Checks if the machine is a testbed.
7693b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng
7703b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng    The signal we use to determine if the machine is a testbed
7713b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng    is if the host attributes contain more than 1 serial.
7723b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng
7733b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng    @param machine: is a list of dicts
7743b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng
7753b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng    @return: True if the machine is a testbed, False otherwise.
7763b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng    """
77705ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    _, afe_host = get_host_info_from_machine(machine)
77805ae2a45397188dd7d7adbc49a61095ebf85e151Kevin Cheng    return len(afe_host.attributes.get('serials', '').split(',')) > 1
77920cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs
78020cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs
78120cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbsdef SetupTsMonGlobalState(*args, **kwargs):
78220cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs    """Import-safe wrap around chromite.lib.ts_mon_config's setup function.
78320cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs
78420cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs    @param *args: Args to pass through.
78520cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs    @param **kwargs: Kwargs to pass through.
78620cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs    """
78716b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    try:
78816b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # TODO(crbug.com/739466) This module import is delayed because it adds
78916b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # 1-2 seconds to the module import time and most users of site_utils
79016b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # don't need it. The correct fix is to break apart site_utils into more
79116b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        # meaningful chunks.
79216b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        from chromite.lib import ts_mon_config
79316b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    except ImportError:
79416b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        logging.warn('Unable to import chromite. Monarch is disabled.')
795930ed9d0a30b546e936254a7c941caf263479781Paul Hobbs        return TrivialContextManager()
79616b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu
79716b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    try:
79816b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        context = ts_mon_config.SetupTsMonGlobalState(*args, **kwargs)
79916b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        if hasattr(context, '__exit__'):
80016b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu            return context
80116b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    except Exception as e:
80216b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu        logging.warning('Caught an exception trying to setup ts_mon, '
80316b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu                        'monitoring is disabled: %s', e, exc_info=True)
80416b46f8039a9e29fa036f1347dcadcacac6a7d77Prathmesh Prabhu    return TrivialContextManager()
80520cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs
80620cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs
80720cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs@contextlib.contextmanager
808604fc875f5d910bb2683680af07adada427be0d4Paul Hobbsdef TrivialContextManager(*args, **kwargs):
809604fc875f5d910bb2683680af07adada427be0d4Paul Hobbs    """Context manager that does nothing.
810604fc875f5d910bb2683680af07adada427be0d4Paul Hobbs
811604fc875f5d910bb2683680af07adada427be0d4Paul Hobbs    @param *args: Ignored args
812604fc875f5d910bb2683680af07adada427be0d4Paul Hobbs    @param **kwargs: Ignored kwargs.
813604fc875f5d910bb2683680af07adada427be0d4Paul Hobbs    """
81420cc72ac289b10e362e2865f4b662425f6ea1a6dPaul Hobbs    yield
8155f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8165f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8175f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Chengdef wait_for_idle_duts(duts, afe, max_wait=IDLE_DUT_WAIT_TIMEOUT):
8185f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    """Wait for the hosts to all go idle.
8195f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8205f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    @param duts: List of duts to check for idle state.
8215f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    @param afe: afe instance.
822ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @param max_wait: Max wait time in seconds to wait for duts to be idle.
8235f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8245f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    @returns Boolean True if all hosts are idle or False if any hosts did not
8255f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng            go idle within max_wait.
8265f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    """
8275f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    start_time = time.time()
8285f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    # We make a shallow copy since we're going to be modifying active_dut_list.
8295f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    active_dut_list = duts[:]
8305f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    while active_dut_list:
8315f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        # Let's rate-limit how often we hit the AFE.
8325f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        time.sleep(1)
8335f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8345f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        # Check if we've waited too long.
8355f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        if (time.time() - start_time) > max_wait:
8365f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng            return False
8375f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8385f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        idle_duts = []
8395f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        # Get the status for the duts and see if they're in the idle state.
8405f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        afe_hosts = afe.get_hosts(active_dut_list)
8415f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        idle_duts = [afe_host.hostname for afe_host in afe_hosts
8425f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng                     if afe_host.status in host_states.IDLE_STATES]
8435f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8445f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        # Take out idle duts so we don't needlessly check them
8455f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        # next time around.
8465f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        for idle_dut in idle_duts:
8475f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng            active_dut_list.remove(idle_dut)
8485f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8495f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        logging.info('still waiting for following duts to go idle: %s',
8505f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng                     active_dut_list)
8515f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    return True
8525f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8535f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8545f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng@contextlib.contextmanager
8555f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Chengdef lock_duts_and_wait(duts, afe, lock_msg='default lock message',
8565f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng                       max_wait=IDLE_DUT_WAIT_TIMEOUT):
8575f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    """Context manager to lock the duts and wait for them to go idle.
8585f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8595f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    @param duts: List of duts to lock.
8605f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    @param afe: afe instance.
86106893307aebfa1836d1b9e9f94043c0d95a3bb9bHidehiko Abe    @param lock_msg: message for afe on locking this host.
862ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @param max_wait: Max wait time in seconds to wait for duts to be idle.
8635f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng
8645f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    @returns Boolean lock_success where True if all duts locked successfully or
8655f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng             False if we timed out waiting too long for hosts to go idle.
8665f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    """
8675f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    try:
8685f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        locked_duts = []
8695f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        duts.sort()
8705f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        for dut in duts:
8715f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng            if afe.lock_host(dut, lock_msg, fail_if_locked=True):
8725f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng                locked_duts.append(dut)
8735f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng            else:
8745f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng                logging.info('%s already locked', dut)
8755f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        yield wait_for_idle_duts(locked_duts, afe, max_wait)
8765f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng    finally:
8775f2ba6cf0b4617abb5e029cd8210942f2c2ec36fKevin Cheng        afe.unlock_hosts(locked_duts)
878b5b8b4f981036971c619b26956c8847140eabd35Dan Shi
879b5b8b4f981036971c619b26956c8847140eabd35Dan Shi
880b5b8b4f981036971c619b26956c8847140eabd35Dan Shidef board_labels_allowed(boards):
881b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    """Check if the list of board labels can be set to a single host.
882b5b8b4f981036971c619b26956c8847140eabd35Dan Shi
883b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    The only case multiple board labels can be set to a single host is for
884b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    testbed, which may have a list of board labels like
885b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    board:angler-1, board:angler-2, board:angler-3, board:marlin-1'
886b5b8b4f981036971c619b26956c8847140eabd35Dan Shi
887b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    @param boards: A list of board labels (may include platform label).
888b5b8b4f981036971c619b26956c8847140eabd35Dan Shi
889b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    @returns True if the the list of boards can be set to a single host.
890b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    """
891b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    # Filter out any non-board labels
892b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    boards = [b for b in boards if re.match('board:.*', b)]
893b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    if len(boards) <= 1:
894b5b8b4f981036971c619b26956c8847140eabd35Dan Shi        return True
895b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    for board in boards:
896b5b8b4f981036971c619b26956c8847140eabd35Dan Shi        if not re.match('board:[^-]+-\d+', board):
897b5b8b4f981036971c619b26956c8847140eabd35Dan Shi            return False
898b5b8b4f981036971c619b26956c8847140eabd35Dan Shi    return True
899ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
900ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
901ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shidef _get_default_size_info(path):
902ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    """Get the default result size information.
903ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
904ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    In case directory summary is failed to build, assume the test result is not
905ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    throttled and all result sizes are the size of existing test results.
906ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
907ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @return: A namedtuple of result size informations, including:
908ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            client_result_collected_KB: The total size (in KB) of test results
909ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    collected from test device. Set to be the total size of the
910ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    given path.
911ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            original_result_total_KB: The original size (in KB) of test results
912ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    before being trimmed. Set to be the total size of the given
913ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    path.
914ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            result_uploaded_KB: The total size (in KB) of test results to be
915ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    uploaded. Set to be the total size of the given path.
916ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            result_throttled: True if test results collection is throttled.
917ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    It's set to False in this default behavior.
918ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    """
919ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    total_size = file_utils.get_directory_size_kibibytes(path);
920ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    return result_utils_lib.ResultSizeInfo(
921ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            client_result_collected_KB=total_size,
922ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            original_result_total_KB=total_size,
923ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            result_uploaded_KB=total_size,
924ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            result_throttled=False)
925ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
926ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
927ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shidef _report_result_size_metrics(result_size_info):
928ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    """Report result sizes information to metrics.
929ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
930ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @param result_size_info: A ResultSizeInfo namedtuple containing information
931ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            of test result sizes.
932ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    """
933ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    fields = {'result_throttled' : result_size_info.result_throttled}
934ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    metrics.Counter(RESULT_METRICS_PREFIX + 'client_result_collected_KB',
935ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    description='The total size (in KB) of test results '
936ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    'collected from test device. Set to be the total size of '
937ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    'the given path.'
938ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    ).increment_by(result_size_info.client_result_collected_KB,
939ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                                   fields=fields)
940ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    metrics.Counter(RESULT_METRICS_PREFIX + 'original_result_total_KB',
941ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    description='The original size (in KB) of test results '
942ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    'before being trimmed.'
943ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    ).increment_by(result_size_info.original_result_total_KB,
944ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                                   fields=fields)
945ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    metrics.Counter(RESULT_METRICS_PREFIX + 'result_uploaded_KB',
946ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    description='The total size (in KB) of test results to be '
947ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    'uploaded.'
948ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                    ).increment_by(result_size_info.result_uploaded_KB,
949ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                                   fields=fields)
950ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
951ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
952ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shidef collect_result_sizes(path, log=logging.debug):
953ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    """Collect the result sizes information and build result summary.
954ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
955ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    It first tries to merge directory summaries and calculate the result sizes
956ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    including:
957ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    client_result_collected_KB: The volume in KB that's transfered from the test
958ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            device.
959ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    original_result_total_KB: The volume in KB that's the original size of the
960ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            result files before being trimmed.
961ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    result_uploaded_KB: The volume in KB that will be uploaded.
962ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    result_throttled: Indicating if the result files were throttled.
963ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
964ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    If directory summary merging failed for any reason, fall back to use the
965ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    total size of the given result directory.
966ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
967ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @param path: Path of the result directory to get size information.
968ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @param log: The logging method, default to logging.debug
969ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    @return: A ResultSizeInfo namedtuple containing information of test result
970ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi             sizes.
971ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    """
972ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    try:
973ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi        client_collected_bytes, summary = result_utils.merge_summaries(path)
974ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi        result_size_info = result_utils_lib.get_result_size_info(
975ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi                client_collected_bytes, summary)
976ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi        html_file = os.path.join(path, result_view.DEFAULT_RESULT_SUMMARY_NAME)
977ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi        result_view.build(client_collected_bytes, summary, html_file)
978ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    except:
979ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi        log('Failed to calculate result sizes based on directory summaries for '
980ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            'directory %s. Fall back to record the total size.\nException: %s' %
981ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi            (path, traceback.format_exc()))
982ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi        result_size_info = _get_default_size_info(path)
983ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
984ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    _report_result_size_metrics(result_size_info)
985ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi
986ffd5b82912ac09842a2b55dd0706343c231bcf3eDan Shi    return result_size_info