184eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
284eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold# Use of this source code is governed by a BSD-style license that can be
384eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold# found in the LICENSE file.
484eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold"""Module for discovering Chrome OS test images and payloads."""
684eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
7bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosaimport logging
81318001bd349930e1a2a152324100583a841a892Gilad Arnoldimport re
984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
10bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosaimport common
11bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosafrom autotest_lib.client.common_lib import global_config
1267dbe59a00e09c5357fd3667487d2ee58dc30993Chris Sosafrom autotest_lib.utils import external_packages
13481fbbf599f4b4491f7b071b777b8b7408b6a901Chris Sosa
1467dbe59a00e09c5357fd3667487d2ee58dc30993Chris Sosafrom autotest_lib.site_utils.autoupdate import import_common
1567dbe59a00e09c5357fd3667487d2ee58dc30993Chris Sosadevserver = import_common.download_and_import('devserver',
1667dbe59a00e09c5357fd3667487d2ee58dc30993Chris Sosa                                              external_packages.DevServerRepo())
17dc41cfe6cb678d92c63b2607101442de845baaedAlex Millerfrom devserver import gsutil_util
18bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa
1984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
2084eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold# A string indicating a zip-file boundary within a URI path. This string must
2184eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold# end with a '/', in order for standard basename code to work correctly for
2284eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold# zip-encapsulated paths.
2384eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad ArnoldZIPFILE_BOUNDARY = '//'
24b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris SosaARCHIVE_URL_FORMAT = '%(archive_prefix)s/%(version)s'
2584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
2684eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
2784eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnoldclass TestImageError(BaseException):
28bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    """Raised on any error in this module."""
2984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    pass
3084eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
3184eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
32728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosaclass NotSingleItem(Exception):
33728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa    """Raised when we want a single item but got multiple."""
34728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa
35728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa
36b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosadef get_default_archive_url(board, build_version):
37b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    """Returns the default archive_url for the given board and build_version .
38fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa
39b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    @param board: the platform/board name
40b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    @param build_version: the full build version i.e. R27-3823.0.0-a2.
41fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa    """
42bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    archive_base = global_config.global_config.get_config_value(
43bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa            'CROS', 'image_storage_server')
44bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    archive_base = archive_base.rstrip('/') # Remove any trailing /'s.
451bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa
461bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    # TODO(garnold) adjustment to -he variant board names; should be removed
471bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    # once we switch to using artifacts from gs://chromeos-images/
481bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    # (see chromium-os:38222)
49bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    board = re.sub('-he$', '_he', board)
50b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    archive_prefix = archive_base + '/%s-release' % board
51bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    return ARCHIVE_URL_FORMAT % dict(
52b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa            archive_prefix=archive_prefix, version=build_version)
53b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa
54b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa
55b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosadef get_archive_url_from_prefix(archive_prefix, build_version):
56b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    """Returns the gs archive_url given a particular archive_prefix.
57b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa
58b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    @param archive_prefix: Use the archive_prefix as the base of your URL
59b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa
60b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa                           construction (instead of config + board-release) e.g.
61b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa                           gs://my_location/my_super_awesome_directory.
62b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    @param build_version: the full build version i.e. R27-3823.0.0-a2.
63b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    """
64b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa    return ARCHIVE_URL_FORMAT % dict(
65b86d2667b0bff0c43e3e26d5d46af6cd0df45c3dChris Sosa            archive_prefix=archive_prefix, version=build_version)
66bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa
67bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa
681bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosadef gs_ls(pattern, archive_url, single):
6984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    """Returns a list of URIs that match a given pattern.
7084eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
711bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    @param pattern: a regexp pattern to match (feeds into re.match).
721bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    @param archive_url: the gs uri where to search (see ARCHIVE_URL_FORMAT).
731bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    @param single: if true, expect a single match and return it.
7484eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
751bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    @return A list of URIs (possibly an empty list).
7684eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
7784eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    """
781bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    try:
791bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa        logging.debug('Searching for pattern %s from url %s', pattern,
801bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa                      archive_url)
811bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa        uri_list = gsutil_util.GetGSNamesWithWait(
82728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa                pattern, archive_url, err_str=__name__, timeout=1)
831bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa        # Convert to the format our clients expect (full archive path).
84fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa        if uri_list:
85728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa            if not single or (single and len(uri_list) == 1):
86728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa                return ['/'.join([archive_url, u]) for u in uri_list]
87728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa            else:
88728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa                raise NotSingleItem()
89fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa
90fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa        return []
911bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    except gsutil_util.PatternNotSpecific as e:
921bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa        raise TestImageError(str(e))
931bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    except gsutil_util.GSUtilError:
941bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa        return []
9584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
9684eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
97fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosadef find_payload_uri(archive_url, delta=False, single=False):
9884eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    """Finds test payloads corresponding to a given board/release.
9984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
100fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa    @param archive_url: Archive_url directory to find the payload.
10184eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    @param delta: if true, seek delta payloads to the given release
10284eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    @param single: if true, expect a single match and return it, otherwise
10384eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold           None
10484eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
10584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    @return A (possibly empty) list of URIs, or a single (possibly None) URI if
10684eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold            |single| is True.
10784eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
10884eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    @raise TestImageError if an error has occurred.
10984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
11084eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    """
111bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    if delta:
112728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa        pattern = '*_delta_*'
113bc69deaf8ce5fb74646f0ff5d32a3773c5c3fbd1Chris Sosa    else:
114728736163fe2ec04c8eb93b27bf4070e5dad1f05Chris Sosa        pattern = '*_full_*'
11584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
1161bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    payload_uri_list = gs_ls(pattern, archive_url, single)
117641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa    if not payload_uri_list:
118641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa        return None if single else []
119641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa
1201bf41c4ff52e8a3ea1a99fa7331a1c77349c9f22Chris Sosa    return payload_uri_list[0] if single else payload_uri_list
12184eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
12284eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
123fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosadef find_image_uri(archive_url):
12484eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    """Returns a URI to a test image.
12584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
126fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa    @param archive_url: archive_url directory to find the payload.
12784eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
12884eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    @return A URI to the desired image if found, None otherwise. It will most
12984eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold            likely be a file inside an image archive (image.zip), in which case
13084eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold            we'll be using ZIPFILE_BOUNDARY ('//') to denote a zip-encapsulated
13184eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold            file, for example:
13284eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold            gs://chromeos-image-archive/.../image.zip//chromiumos_test_image.bin
13384eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
13484eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    @raise TestImageError if an error has occurred.
13584eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold
13684eb60cb5d66d6052407ebae4f1aa38000c9cbcfGilad Arnold    """
137641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa    image_archive = gs_ls('image.zip', archive_url, single=True)
138641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa    if not image_archive:
139641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa        return None
140641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa
141641c929a4205a630d9065332c7a30c5ef6c30cd3Chris Sosa    return (image_archive[0] + ZIPFILE_BOUNDARY + 'chromiumos_test_image.bin')
142