site_utils.py revision 7f215d3906ac204694d64b7eaa85155777f88f39
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
63cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport httplib
73cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport json
8a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shiimport logging
935d661e09666d315325f8942d06949ca7283666fMK Ryuimport os
10023afc65f9377db51ff6122977a9f529a32422d3beepsimport random
11dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Millerimport re
123cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport time
13bef578d9163c6574d626e8b98298c59a5ab79221Paul Drewsimport urllib2
14a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
153cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteimport common
16023afc65f9377db51ff6122977a9f529a32422d3beepsfrom autotest_lib.client.common_lib import base_utils
17023afc65f9377db51ff6122977a9f529a32422d3beepsfrom autotest_lib.client.common_lib import error
18023afc65f9377db51ff6122977a9f529a32422d3beepsfrom autotest_lib.client.common_lib import global_config
190c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryufrom autotest_lib.client.common_lib import host_queue_entry_states
20a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shifrom autotest_lib.server.cros.dynamic_suite import constants
217e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shifrom autotest_lib.server.cros.dynamic_suite import job_status
22a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
2382997b9a9791105d81c9db14e75ed14946f78f94Dan Shitry:
2482997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    from chromite.lib import cros_build_lib
2582997b9a9791105d81c9db14e75ed14946f78f94Dan Shiexcept ImportError:
2682997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    logging.warn('Unable to import chromite.')
2782997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    # Init the module variable to None. Access to this module can check if it
2882997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    # is not None before making calls.
2982997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    cros_build_lib = None
3082997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
31a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
32dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller_SHERIFF_JS = global_config.global_config.get_config_value(
33dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    'NOTIFICATIONS', 'sheriffs', default='')
343197b39f82eb92afff33c7d44b805afe120c7627Fang Deng_LAB_SHERIFF_JS = global_config.global_config.get_config_value(
353197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    'NOTIFICATIONS', 'lab_sheriffs', default='')
36dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller_CHROMIUM_BUILD_URL = global_config.global_config.get_config_value(
37dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    'NOTIFICATIONS', 'chromium_build_url', default='')
38dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
393cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard BarnetteLAB_GOOD_STATES = ('open', 'throttled')
403cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
413cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
42abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnetteclass TestLabException(Exception):
43abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    """Exception raised when the Test Lab blocks a test or suite."""
443cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    pass
453cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
463cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
473cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnetteclass ParseBuildNameException(Exception):
483cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """Raised when ParseBuildName() cannot parse a build name."""
493cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    pass
503cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
513cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
523cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnettedef ParseBuildName(name):
533cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """Format a build name, given board, type, milestone, and manifest num.
543cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
55b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi    @param name: a build name, e.g. 'x86-alex-release/R20-2015.0.0' or a
56b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                 relative build name, e.g. 'x86-alex-release/LATEST'
573cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
583cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    @return board: board the manifest is for, e.g. x86-alex.
593cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    @return type: one of 'release', 'factory', or 'firmware'
603cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    @return milestone: (numeric) milestone the manifest was associated with.
61b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                        Will be None for relative build names.
62b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi    @return manifest: manifest number, e.g. '2015.0.0'.
63b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                      Will be None for relative build names.
643cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
653cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """
66f8f648e049ca2f8777fa55c0ea9c4f886efecea9Simran Basi    match = re.match(r'(trybot-)?(?P<board>[\w-]+)-(?P<type>\w+)/'
67f8f648e049ca2f8777fa55c0ea9c4f886efecea9Simran Basi                     r'(R(?P<milestone>\d+)-(?P<manifest>[\d.ab-]+)|LATEST)',
68f8f648e049ca2f8777fa55c0ea9c4f886efecea9Simran Basi                     name)
69f8f648e049ca2f8777fa55c0ea9c4f886efecea9Simran Basi    if match and len(match.groups()) >= 5:
70b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi        return (match.group('board'), match.group('type'),
71b7d2116ad62dd09c52791c8f27dfa61b9df186b9Simran Basi                match.group('milestone'), match.group('manifest'))
723cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    raise ParseBuildNameException('%s is a malformed build name.' % name)
733cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
74dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
75a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shidef get_label_from_afe(hostname, label_prefix, afe):
76a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """Retrieve a host's specific label from the AFE.
77a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
78a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    Looks for a host label that has the form <label_prefix>:<value>
79a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    and returns the "<value>" part of the label. None is returned
80a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    if there is not a label matching the pattern
81a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
82a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param hostname: hostname of given DUT.
83a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param label_prefix: prefix of label to be matched, e.g., |board:|
84a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param afe: afe instance.
85a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @returns the label that matches the prefix or 'None'
86a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
87a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """
88a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    labels = afe.get_labels(name__startswith=label_prefix,
89a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi                            host__hostname__in=[hostname])
90a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    if labels and len(labels) == 1:
91a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi        return labels[0].name.split(label_prefix, 1)[1]
92a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
93a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
94a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shidef get_board_from_afe(hostname, afe):
95a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """Retrieve given host's board from its labels in the AFE.
96a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
97a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    Looks for a host label of the form "board:<board>", and
98a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    returns the "<board>" part of the label.  `None` is returned
99a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    if there is not a single, unique label matching the pattern.
100a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
101a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param hostname: hostname of given DUT.
102a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param afe: afe instance.
103a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @returns board from label, or `None`.
104a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
105a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """
106a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    return get_label_from_afe(hostname, constants.BOARD_PREFIX, afe)
107a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
108a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
109a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shidef get_build_from_afe(hostname, afe):
110a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """Retrieve the current build for given host from the AFE.
111a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
112a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    Looks through the host's labels in the AFE to determine its build.
113a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
114a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param hostname: hostname of given DUT.
115a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @param afe: afe instance.
116a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    @returns The current build or None if it could not find it or if there
117a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi             were multiple build labels assigned to this host.
118a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
119a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    """
120a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi    return get_label_from_afe(hostname, constants.VERSION_PREFIX, afe)
121a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
122a1ecd5c903928f359cd6cbcff5c986652e109599Dan Shi
1233197b39f82eb92afff33c7d44b805afe120c7627Fang Dengdef get_sheriffs(lab_only=False):
124dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    """
125dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    Polls the javascript file that holds the identity of the sheriff and
126dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    parses it's output to return a list of chromium sheriff email addresses.
127dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    The javascript file can contain the ldap of more than one sheriff, eg:
128dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    document.write('sheriff_one, sheriff_two').
129dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller
1303197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    @param lab_only: if True, only pulls lab sheriff.
1313197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    @return: A list of chroium.org sheriff email addresses to cc on the bug.
1323197b39f82eb92afff33c7d44b805afe120c7627Fang Deng             An empty list if failed to parse the javascript.
133dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    """
134dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    sheriff_ids = []
1353197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    sheriff_js_list = _LAB_SHERIFF_JS.split(',')
1363197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    if not lab_only:
1373197b39f82eb92afff33c7d44b805afe120c7627Fang Deng        sheriff_js_list.extend(_SHERIFF_JS.split(','))
1383197b39f82eb92afff33c7d44b805afe120c7627Fang Deng
1393197b39f82eb92afff33c7d44b805afe120c7627Fang Deng    for sheriff_js in sheriff_js_list:
140dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller        try:
141dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            url_content = base_utils.urlopen('%s%s'% (
142dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller                _CHROMIUM_BUILD_URL, sheriff_js)).read()
143dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller        except (ValueError, IOError) as e:
1444efdf03d1057f9d38e043b7c2affe842109805d2beeps            logging.warning('could not parse sheriff from url %s%s: %s',
1454efdf03d1057f9d38e043b7c2affe842109805d2beeps                             _CHROMIUM_BUILD_URL, sheriff_js, str(e))
146bef578d9163c6574d626e8b98298c59a5ab79221Paul Drews        except (urllib2.URLError, httplib.HTTPException) as e:
147bef578d9163c6574d626e8b98298c59a5ab79221Paul Drews            logging.warning('unexpected error reading from url "%s%s": %s',
148bef578d9163c6574d626e8b98298c59a5ab79221Paul Drews                             _CHROMIUM_BUILD_URL, sheriff_js, str(e))
149dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller        else:
150dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            ldaps = re.search(r"document.write\('(.*)'\)", url_content)
151dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            if not ldaps:
1524efdf03d1057f9d38e043b7c2affe842109805d2beeps                logging.warning('Could not retrieve sheriff ldaps for: %s',
1534efdf03d1057f9d38e043b7c2affe842109805d2beeps                                 url_content)
154dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller                continue
155dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller            sheriff_ids += ['%s@chromium.org' % alias.replace(' ', '')
156dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller                            for alias in ldaps.group(1).split(',')]
157dadc2c21c0b0b904067ea6400e179a187c0247d4Alex Miller    return sheriff_ids
15846dadc9439355f72d394dcc4700902001bd797ffbeeps
15946dadc9439355f72d394dcc4700902001bd797ffbeeps
16046dadc9439355f72d394dcc4700902001bd797ffbeepsdef remote_wget(source_url, dest_path, ssh_cmd):
16146dadc9439355f72d394dcc4700902001bd797ffbeeps    """wget source_url from localhost to dest_path on remote host using ssh.
16246dadc9439355f72d394dcc4700902001bd797ffbeeps
16346dadc9439355f72d394dcc4700902001bd797ffbeeps    @param source_url: The complete url of the source of the package to send.
16446dadc9439355f72d394dcc4700902001bd797ffbeeps    @param dest_path: The path on the remote host's file system where we would
16546dadc9439355f72d394dcc4700902001bd797ffbeeps        like to store the package.
16646dadc9439355f72d394dcc4700902001bd797ffbeeps    @param ssh_cmd: The ssh command to use in performing the remote wget.
16746dadc9439355f72d394dcc4700902001bd797ffbeeps    """
16846dadc9439355f72d394dcc4700902001bd797ffbeeps    wget_cmd = ("wget -O - %s | %s 'cat >%s'" %
16946dadc9439355f72d394dcc4700902001bd797ffbeeps                (source_url, ssh_cmd, dest_path))
17046dadc9439355f72d394dcc4700902001bd797ffbeeps    base_utils.run(wget_cmd)
17146dadc9439355f72d394dcc4700902001bd797ffbeeps
1723cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
173266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette_MAX_LAB_STATUS_ATTEMPTS = 5
174266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnettedef _get_lab_status(status_url):
1753cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """Grabs the current lab status and message.
1763cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
177266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    @returns The JSON object obtained from the given URL.
178266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
1793cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """
1803cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    retry_waittime = 1
181266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    for _ in range(_MAX_LAB_STATUS_ATTEMPTS):
1823cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        try:
1833cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette            response = urllib2.urlopen(status_url)
1843cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        except IOError as e:
185266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette            logging.debug('Error occurred when grabbing the lab status: %s.',
1863cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette                          e)
1873cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette            time.sleep(retry_waittime)
1883cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette            continue
1893cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        # Check for successful response code.
1903cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        if response.getcode() == 200:
191266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette            return json.load(response)
1923cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette        time.sleep(retry_waittime)
193266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    return None
1943cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
1953cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
196abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnettedef _decode_lab_status(lab_status, build):
197266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    """Decode lab status, and report exceptions as needed.
1983cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
199abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    Take a deserialized JSON object from the lab status page, and
200abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    interpret it to determine the actual lab status.  Raise
201266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    exceptions as required to report when the lab is down.
2023cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
203abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @param build: build name that we want to check the status of.
2043cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
205abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @raises TestLabException Raised if a request to test for the given
206abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                             status and build should be blocked.
2073cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    """
2083cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    # First check if the lab is up.
209266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    if not lab_status['general_state'] in LAB_GOOD_STATES:
210abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette        raise TestLabException('Chromium OS Test Lab is closed: '
211abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                               '%s.' % lab_status['message'])
2123cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette
213abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    # Check if the build we wish to use is disabled.
2143cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    # Lab messages should be in the format of:
215abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    #    Lab is 'status' [regex ...] (comment)
216abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    # If the build name matches any regex, it will be blocked.
217abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    build_exceptions = re.search('\[(.*)\]', lab_status['message'])
218ae43721d4a11c6ecd2d502bba73b185b05bfce29Prashanth Balasubramanian    if not build_exceptions or not build:
219abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette        return
220abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    for build_pattern in build_exceptions.group(1).split():
2217f215d3906ac204694d64b7eaa85155777f88f39J. Richard Barnette        if re.match(build_pattern, build):
222abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette            raise TestLabException('Chromium OS Test Lab is closed: '
223abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                                   '%s matches %s.' % (
224abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                                           build, build_pattern))
2253cbd76b06c383d8bc3e4ba959659a0bd04180761J. Richard Barnette    return
226266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
227266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
22894234cb747dc067b159789a4b63f21b23d706922Dan Shidef is_in_lab():
22994234cb747dc067b159789a4b63f21b23d706922Dan Shi    """Check if current Autotest instance is in lab
23094234cb747dc067b159789a4b63f21b23d706922Dan Shi
23194234cb747dc067b159789a4b63f21b23d706922Dan Shi    @return: True if the Autotest instance is in lab.
23294234cb747dc067b159789a4b63f21b23d706922Dan Shi    """
23394234cb747dc067b159789a4b63f21b23d706922Dan Shi    test_server_name = global_config.global_config.get_config_value(
23494234cb747dc067b159789a4b63f21b23d706922Dan Shi              'SERVER', 'hostname')
23594234cb747dc067b159789a4b63f21b23d706922Dan Shi    return test_server_name.startswith('cautotest')
23694234cb747dc067b159789a4b63f21b23d706922Dan Shi
23794234cb747dc067b159789a4b63f21b23d706922Dan Shi
238abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnettedef check_lab_status(build):
239abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    """Check if the lab status allows us to schedule for a build.
240266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
241abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    Checks if the lab is down, or if testing for the requested build
242abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    should be blocked.
243266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
244abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @param build: Name of the build to be scheduled for testing.
245266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
246abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    @raises TestLabException Raised if a request to test for the given
247abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette                             status and build should be blocked.
248266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
249266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    """
250266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    # Ensure we are trying to schedule on the actual lab.
25194234cb747dc067b159789a4b63f21b23d706922Dan Shi    if not is_in_lab():
252266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette        return
253266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette
254266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    # Download the lab status from its home on the web.
255266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    status_url = global_config.global_config.get_config_value(
256266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette            'CROS', 'lab_status_url')
257266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    json_status = _get_lab_status(status_url)
258266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette    if json_status is None:
259266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette        # We go ahead and say the lab is open if we can't get the status.
26004be2bd5e4666a5c253e9c30ab20555e04286032Ilja H. Friedel        logging.warning('Could not get a status from %s', status_url)
261266da2a226391a7153df264d54dcb54635df7bfcJ. Richard Barnette        return
262abbe09600d73d77af92157027a20661c035be01eJ. Richard Barnette    _decode_lab_status(json_status, build)
263023afc65f9377db51ff6122977a9f529a32422d3beeps
264023afc65f9377db51ff6122977a9f529a32422d3beeps
265023afc65f9377db51ff6122977a9f529a32422d3beepsdef lock_host_with_labels(afe, lock_manager, labels):
266023afc65f9377db51ff6122977a9f529a32422d3beeps    """Lookup and lock one host that matches the list of input labels.
267023afc65f9377db51ff6122977a9f529a32422d3beeps
268023afc65f9377db51ff6122977a9f529a32422d3beeps    @param afe: An instance of the afe class, as defined in server.frontend.
269023afc65f9377db51ff6122977a9f529a32422d3beeps    @param lock_manager: A lock manager capable of locking hosts, eg the
270023afc65f9377db51ff6122977a9f529a32422d3beeps        one defined in server.cros.host_lock_manager.
271023afc65f9377db51ff6122977a9f529a32422d3beeps    @param labels: A list of labels to look for on hosts.
272023afc65f9377db51ff6122977a9f529a32422d3beeps
273023afc65f9377db51ff6122977a9f529a32422d3beeps    @return: The hostname of a host matching all labels, and locked through the
274023afc65f9377db51ff6122977a9f529a32422d3beeps        lock_manager. The hostname will be as specified in the database the afe
275023afc65f9377db51ff6122977a9f529a32422d3beeps        object is associated with, i.e if it exists in afe_hosts with a .cros
276023afc65f9377db51ff6122977a9f529a32422d3beeps        suffix, the hostname returned will contain a .cros suffix.
277023afc65f9377db51ff6122977a9f529a32422d3beeps
278023afc65f9377db51ff6122977a9f529a32422d3beeps    @raises: error.NoEligibleHostException: If no hosts matching the list of
279023afc65f9377db51ff6122977a9f529a32422d3beeps        input labels are available.
280023afc65f9377db51ff6122977a9f529a32422d3beeps    @raises: error.TestError: If unable to lock a host matching the labels.
281023afc65f9377db51ff6122977a9f529a32422d3beeps    """
282023afc65f9377db51ff6122977a9f529a32422d3beeps    potential_hosts = afe.get_hosts(multiple_labels=labels)
283023afc65f9377db51ff6122977a9f529a32422d3beeps    if not potential_hosts:
284023afc65f9377db51ff6122977a9f529a32422d3beeps        raise error.NoEligibleHostException(
285023afc65f9377db51ff6122977a9f529a32422d3beeps                'No devices found with labels %s.' % labels)
286023afc65f9377db51ff6122977a9f529a32422d3beeps
287023afc65f9377db51ff6122977a9f529a32422d3beeps    # This prevents errors where a fault might seem repeatable
288023afc65f9377db51ff6122977a9f529a32422d3beeps    # because we lock, say, the same packet capturer for each test run.
289023afc65f9377db51ff6122977a9f529a32422d3beeps    random.shuffle(potential_hosts)
290023afc65f9377db51ff6122977a9f529a32422d3beeps    for host in potential_hosts:
291023afc65f9377db51ff6122977a9f529a32422d3beeps        if lock_manager.lock([host.hostname]):
292023afc65f9377db51ff6122977a9f529a32422d3beeps            logging.info('Locked device %s with labels %s.',
293023afc65f9377db51ff6122977a9f529a32422d3beeps                         host.hostname, labels)
294023afc65f9377db51ff6122977a9f529a32422d3beeps            return host.hostname
295023afc65f9377db51ff6122977a9f529a32422d3beeps        else:
296023afc65f9377db51ff6122977a9f529a32422d3beeps            logging.info('Unable to lock device %s with labels %s.',
297023afc65f9377db51ff6122977a9f529a32422d3beeps                         host.hostname, labels)
298023afc65f9377db51ff6122977a9f529a32422d3beeps
299023afc65f9377db51ff6122977a9f529a32422d3beeps    raise error.TestError('Could not lock a device with labels %s' % labels)
3007e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3017e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3027e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shidef get_test_views_from_tko(suite_job_id, tko):
3037e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    """Get test name and result for given suite job ID.
3047e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3057e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @param suite_job_id: ID of suite job.
3067e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @param tko: an instance of TKO as defined in server/frontend.py.
3077e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @return: A dictionary of test status keyed by test name, e.g.,
3087e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi             {'dummy_Fail.Error': 'ERROR', 'dummy_Fail.NAError': 'TEST_NA'}
3097e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    @raise: Exception when there is no test view found.
3107e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3117e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    """
3127e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    views = tko.run('get_detailed_test_views', afe_job_id=suite_job_id)
3137e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    relevant_views = filter(job_status.view_is_relevant, views)
3147e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    if not relevant_views:
3157e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi        raise Exception('Failed to retrieve job results.')
3167e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3177e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    test_views = {}
3187e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    for view in relevant_views:
3197e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi        test_views[view['test_name']] = view['status']
3207e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi
3217e04fa8cf1f07512f52870cf4bffd9f8b0801088Dan Shi    return test_views
32235d661e09666d315325f8942d06949ca7283666fMK Ryu
32335d661e09666d315325f8942d06949ca7283666fMK Ryu
32435d661e09666d315325f8942d06949ca7283666fMK Ryudef parse_simple_config(config_file):
32535d661e09666d315325f8942d06949ca7283666fMK Ryu    """Get paths by parsing a simple config file.
32635d661e09666d315325f8942d06949ca7283666fMK Ryu
32735d661e09666d315325f8942d06949ca7283666fMK Ryu    Each line of the config file is a path for a file or directory.
32835d661e09666d315325f8942d06949ca7283666fMK Ryu    Ignore an empty line and a line starting with a hash character ('#').
32935d661e09666d315325f8942d06949ca7283666fMK Ryu    One example of this kind of simple config file is
33035d661e09666d315325f8942d06949ca7283666fMK Ryu    client/common_lib/logs_to_collect.
33135d661e09666d315325f8942d06949ca7283666fMK Ryu
33235d661e09666d315325f8942d06949ca7283666fMK Ryu    @param config_file: Config file path
33335d661e09666d315325f8942d06949ca7283666fMK Ryu    @return: A list of directory strings
33435d661e09666d315325f8942d06949ca7283666fMK Ryu    """
33535d661e09666d315325f8942d06949ca7283666fMK Ryu    dirs = []
33635d661e09666d315325f8942d06949ca7283666fMK Ryu    for l in open(config_file):
33735d661e09666d315325f8942d06949ca7283666fMK Ryu        l = l.strip()
33835d661e09666d315325f8942d06949ca7283666fMK Ryu        if l and not l.startswith('#'):
33935d661e09666d315325f8942d06949ca7283666fMK Ryu            dirs.append(l)
34035d661e09666d315325f8942d06949ca7283666fMK Ryu    return dirs
34135d661e09666d315325f8942d06949ca7283666fMK Ryu
34235d661e09666d315325f8942d06949ca7283666fMK Ryu
34335d661e09666d315325f8942d06949ca7283666fMK Ryudef concat_path_except_last(base, sub):
34435d661e09666d315325f8942d06949ca7283666fMK Ryu    """Concatenate two paths but exclude last entry.
34535d661e09666d315325f8942d06949ca7283666fMK Ryu
34635d661e09666d315325f8942d06949ca7283666fMK Ryu    Take two paths as parameters and return a path string in which
34735d661e09666d315325f8942d06949ca7283666fMK Ryu    the second path becomes under the first path.
34835d661e09666d315325f8942d06949ca7283666fMK Ryu    In addition, remove the last path entry from the concatenated path.
34935d661e09666d315325f8942d06949ca7283666fMK Ryu    This works even when two paths are absolute paths.
35035d661e09666d315325f8942d06949ca7283666fMK Ryu
35135d661e09666d315325f8942d06949ca7283666fMK Ryu    e.g., /usr/local/autotest/results/ + /var/log/ =
35235d661e09666d315325f8942d06949ca7283666fMK Ryu    /usr/local/autotest/results/var
35335d661e09666d315325f8942d06949ca7283666fMK Ryu
35435d661e09666d315325f8942d06949ca7283666fMK Ryu    e.g., /usr/local/autotest/results/ + /var/log/syslog =
35535d661e09666d315325f8942d06949ca7283666fMK Ryu    /usr/local/autotest/results/var/log
35635d661e09666d315325f8942d06949ca7283666fMK Ryu
35735d661e09666d315325f8942d06949ca7283666fMK Ryu    @param base: Beginning path
35835d661e09666d315325f8942d06949ca7283666fMK Ryu    @param sub: The path that is concatenated to base
35935d661e09666d315325f8942d06949ca7283666fMK Ryu    @return: Concatenated path string
36035d661e09666d315325f8942d06949ca7283666fMK Ryu    """
36135d661e09666d315325f8942d06949ca7283666fMK Ryu    dirname = os.path.dirname(sub.rstrip('/'))
36235d661e09666d315325f8942d06949ca7283666fMK Ryu    return os.path.join(base, dirname.strip('/'))
363c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu
364c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu
365c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryudef get_data_key(prefix, suite, build, board):
366c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    """
367c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    Constructs a key string from parameters.
368c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu
369c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param prefix: Prefix for the generating key.
370c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param suite: a suite name. e.g., bvt-cq, bvt-inline, dummy
371c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param build: The build string. This string should have a consistent
372c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        format eg: x86-mario-release/R26-3570.0.0. If the format of this
373c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        string changes such that we can't determine build_type or branch
374c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        we give up and use the parametes we're sure of instead (suite,
375c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        board). eg:
376c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            1. build = x86-alex-pgo-release/R26-3570.0.0
377c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               branch = 26
378c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               build_type = pgo-release
379c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            2. build = lumpy-paladin/R28-3993.0.0-rc5
380c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               branch = 28
381c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu               build_type = paladin
382c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @param board: The board that this suite ran on.
383c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    @return: The key string used for a dictionary.
384c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    """
385c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    try:
386c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        _board, build_type, branch = ParseBuildName(build)[:3]
387c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    except ParseBuildNameException as e:
388c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        logging.error(str(e))
389c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        branch = 'Unknown'
390c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        build_type = 'Unknown'
391c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    else:
392c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        embedded_str = re.search(r'x86-\w+-(.*)', _board)
393c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        if embedded_str:
394c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            build_type = embedded_str.group(1) + '-' + build_type
395c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu
396c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    data_key_dict = {
397c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'prefix': prefix,
398c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'board': board,
399c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'branch': branch,
400c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'build_type': build_type,
401c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu        'suite': suite,
402c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    }
403c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu    return ('%(prefix)s.%(board)s.%(build_type)s.%(branch)s.%(suite)s'
404c9c0c3ff2e8800bfad3f0c79fe5879be0ec78489MK Ryu            % data_key_dict)
40583184356b60f4352e46e69488d54222032d426c0MK Ryu
40683184356b60f4352e46e69488d54222032d426c0MK Ryu
4072d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryudef setup_logging(logfile=None, prefix=False):
40883184356b60f4352e46e69488d54222032d426c0MK Ryu    """Setup basic logging with all logging info stripped.
40983184356b60f4352e46e69488d54222032d426c0MK Ryu
41083184356b60f4352e46e69488d54222032d426c0MK Ryu    Calls to logging will only show the message. No severity is logged.
41183184356b60f4352e46e69488d54222032d426c0MK Ryu
41283184356b60f4352e46e69488d54222032d426c0MK Ryu    @param logfile: If specified dump output to a file as well.
4132d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    @param prefix: Flag for log prefix. Set to True to add prefix to log
4142d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        entries to include timestamp and log level. Default is False.
41583184356b60f4352e46e69488d54222032d426c0MK Ryu    """
41683184356b60f4352e46e69488d54222032d426c0MK Ryu    # Remove all existing handlers. client/common_lib/logging_config adds
41783184356b60f4352e46e69488d54222032d426c0MK Ryu    # a StreamHandler to logger when modules are imported, e.g.,
41883184356b60f4352e46e69488d54222032d426c0MK Ryu    # autotest_lib.client.bin.utils. A new StreamHandler will be added here to
41983184356b60f4352e46e69488d54222032d426c0MK Ryu    # log only messages, not severity.
42083184356b60f4352e46e69488d54222032d426c0MK Ryu    logging.getLogger().handlers = []
42183184356b60f4352e46e69488d54222032d426c0MK Ryu
4222d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    if prefix:
4232d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        log_format = '%(asctime)s %(levelname)-5s| %(message)s'
4242d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    else:
4252d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        log_format = '%(message)s'
4262d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu
42783184356b60f4352e46e69488d54222032d426c0MK Ryu    screen_handler = logging.StreamHandler()
4282d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu    screen_handler.setFormatter(logging.Formatter(log_format))
42983184356b60f4352e46e69488d54222032d426c0MK Ryu    logging.getLogger().addHandler(screen_handler)
43083184356b60f4352e46e69488d54222032d426c0MK Ryu    logging.getLogger().setLevel(logging.INFO)
43183184356b60f4352e46e69488d54222032d426c0MK Ryu    if logfile:
43283184356b60f4352e46e69488d54222032d426c0MK Ryu        file_handler = logging.FileHandler(logfile)
4332d0a364b21297418bef4d7f32acee8831e4eeefeMK Ryu        file_handler.setFormatter(logging.Formatter(log_format))
43483184356b60f4352e46e69488d54222032d426c0MK Ryu        file_handler.setLevel(logging.DEBUG)
43583184356b60f4352e46e69488d54222032d426c0MK Ryu        logging.getLogger().addHandler(file_handler)
4368c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4378c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4388c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramaniandef is_shard():
4398c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    """Determines if this instance is running as a shard.
4408c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4418c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    Reads the global_config value shard_hostname in the section SHARD.
4428c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian
4438c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    @return True, if shard_hostname is set, False otherwise.
4448c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    """
4458c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian    hostname = global_config.global_config.get_config_value(
4468c98ac10beaa08bfb975c412b0b3bda23178763aPrashanth Balasubramanian            'SHARD', 'shard_hostname', default=None)
4470c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return bool(hostname)
4480c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4490c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4500c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_special_task_status(is_complete, success, is_active):
4510c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Get the status of a special task.
4520c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4530c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    Emulate a host queue entry status for a special task
4540c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    Although SpecialTasks are not HostQueueEntries, it is helpful to
4550c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    the user to present similar statuses.
4560c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4570c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param is_complete    Boolean if the task is completed.
4580c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param success        Boolean if the task succeeded.
4590c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param is_active      Boolean if the task is active.
4600c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4610c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @return The status of a special task.
4620c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
4630c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    if is_complete:
4640c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        if success:
4650c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu            return host_queue_entry_states.Status.COMPLETED
4660c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        return host_queue_entry_states.Status.FAILED
4670c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    if is_active:
4680c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        return host_queue_entry_states.Status.RUNNING
4690c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return host_queue_entry_states.Status.QUEUED
4700c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4710c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4720c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_special_task_exec_path(hostname, task_id, task_name, time_requested):
4730c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Get the execution path of the SpecialTask.
4740c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4750c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    This method returns different paths depending on where a
4760c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    the task ran:
4770c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        * Master: hosts/hostname/task_id-task_type
4780c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        * Shard: Master_path/time_created
4790c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    This is to work around the fact that a shard can fail independent
4800c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    of the master, and be replaced by another shard that has the same
4810c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    hosts. Without the time_created stamp the logs of the tasks running
4820c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    on the second shard will clobber the logs from the first in google
4830c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    storage, because task ids are not globally unique.
4840c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4850c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param hostname        Hostname
4860c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param task_id         Special task id
4870c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param task_name       Special task name (e.g., Verify, Repair, etc)
4880c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param time_requested  Special task requested time.
4890c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4900c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @return An execution path for the task.
4910c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
4920c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    results_path = 'hosts/%s/%s-%s' % (hostname, task_id, task_name.lower())
4930c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
4940c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # If we do this on the master it will break backward compatibility,
4950c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # as there are tasks that currently don't have timestamps. If a host
4960c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # or job has been sent to a shard, the rpc for that host/job will
4970c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # be redirected to the shard, so this global_config check will happen
4980c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # on the shard the logs are on.
4990c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    if not is_shard():
5000c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu        return results_path
5010c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5020c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # Generate a uid to disambiguate special task result directories
5030c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # in case this shard fails. The simplest uid is the job_id, however
5040c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # in rare cases tasks do not have jobs associated with them (eg:
5050c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # frontend verify), so just use the creation timestamp. The clocks
5060c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # between a shard and master should always be in sync. Any discrepancies
5070c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # will be brought to our attention in the form of job timeouts.
5080c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    uid = time_requested.strftime('%Y%d%m%H%M%S')
5090c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5100c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # TODO: This is a hack, however it is the easiest way to achieve
5110c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # correctness. There is currently some debate over the future of
5120c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # tasks in our infrastructure and refactoring everything right
5130c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    # now isn't worth the time.
5140c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return '%s/%s' % (results_path, uid)
5150c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5160c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5170c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_job_tag(id, owner):
5180c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Returns a string tag for a job.
5190c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5200c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param id    Job id
5210c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param owner Job owner
5220c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5230c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
5240c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return '%s-%s' % (id, owner)
5250c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5260c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5270c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryudef get_hqe_exec_path(tag, execution_subdir):
5280c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """Returns a execution path to a HQE's results.
5290c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5300c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param tag               Tag string for a job associated with a HQE.
5310c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    @param execution_subdir  Execution sub-directory string of a HQE.
5320c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu
5330c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    """
5340c1a37dd9b1237fe8d43c7f911ce601104806339MK Ryu    return os.path.join(tag, execution_subdir)
53582997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
53682997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
53782997b9a9791105d81c9db14e75ed14946f78f94Dan Shidef is_inside_chroot():
53882997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    """Check if the process is running inside chroot.
53982997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
54082997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    This is a wrapper around chromite.lib.cros_build_lib.IsInsideChroot(). The
54182997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    method checks if cros_build_lib can be imported first.
54282997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
54382997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    @return: True if the process is running inside chroot or cros_build_lib
54482997b9a9791105d81c9db14e75ed14946f78f94Dan Shi             cannot be imported.
54582997b9a9791105d81c9db14e75ed14946f78f94Dan Shi
54682997b9a9791105d81c9db14e75ed14946f78f94Dan Shi    """
5477f215d3906ac204694d64b7eaa85155777f88f39J. Richard Barnette    return not cros_build_lib or cros_build_lib.IsInsideChroot()
548