test_image.py revision fec1349a3e70eb41dce8bc07a8c63563d23d64b2
1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for discovering Chrome OS test images and payloads."""
6
7import logging
8import os
9import re
10
11import common
12from autotest_lib.client.common_lib import global_config
13
14try:
15  from devserver import gsutil_util
16except ImportError:
17  # Make this easy for users to automatically import the devserver if not found.
18  from autotest_lib.utils import build_externals, external_packages
19  tot = external_packages.find_top_of_autotest_tree()
20  install_dir = os.path.join(tot, build_externals.INSTALL_DIR)
21  build_externals.build_and_install_packages(
22      [external_packages.DevServerRepo()], install_dir)
23  from devserver import gsutil_util
24
25
26# A string indicating a zip-file boundary within a URI path. This string must
27# end with a '/', in order for standard basename code to work correctly for
28# zip-encapsulated paths.
29ZIPFILE_BOUNDARY = '//'
30ARCHIVE_URL_FORMAT = '%(archive_base)s/%(board)s-release/%(branch)s-%(release)s'
31
32
33class TestImageError(BaseException):
34    """Raised on any error in this module."""
35    pass
36
37
38def get_archive_url(board, branch, release):
39    """Returns the gs archive_url for the respective arguments.
40
41    @param board: the platform name (string)
42    @param release: the release version (string), without milestone and
43                    attempt/build counters
44    @param branch: the release's branch name
45    """
46    archive_base = global_config.global_config.get_config_value(
47            'CROS', 'image_storage_server')
48    archive_base = archive_base.rstrip('/') # Remove any trailing /'s.
49
50    # TODO(garnold) adjustment to -he variant board names; should be removed
51    # once we switch to using artifacts from gs://chromeos-images/
52    # (see chromium-os:38222)
53    board = re.sub('-he$', '_he', board)
54    return ARCHIVE_URL_FORMAT % dict(
55            archive_base=archive_base, board=board, branch=branch,
56            release=release)
57
58
59def gs_ls(pattern, archive_url, single):
60    """Returns a list of URIs that match a given pattern.
61
62    @param pattern: a regexp pattern to match (feeds into re.match).
63    @param archive_url: the gs uri where to search (see ARCHIVE_URL_FORMAT).
64    @param single: if true, expect a single match and return it.
65
66    @return A list of URIs (possibly an empty list).
67
68    """
69    try:
70        logging.debug('Searching for pattern %s from url %s', pattern,
71                      archive_url)
72        uri_list = gsutil_util.GetGSNamesWithWait(
73                pattern, archive_url, err_str=__name__, single_item=single,
74                timeout=1)
75        # Convert to the format our clients expect (full archive path).
76        if uri_list:
77            return ['/'.join([archive_url, u]) for u in uri_list]
78
79        return []
80    except gsutil_util.PatternNotSpecific as e:
81        raise TestImageError(str(e))
82    except gsutil_util.GSUtilError:
83        return []
84
85
86def find_payload_uri(archive_url, delta=False, single=False):
87    """Finds test payloads corresponding to a given board/release.
88
89    @param archive_url: Archive_url directory to find the payload.
90    @param delta: if true, seek delta payloads to the given release
91    @param single: if true, expect a single match and return it, otherwise
92           None
93
94    @return A (possibly empty) list of URIs, or a single (possibly None) URI if
95            |single| is True.
96
97    @raise TestImageError if an error has occurred.
98
99    """
100    if delta:
101        pattern = '.*_delta_.*'
102    else:
103        pattern = '.*_full_.*'
104
105    payload_uri_list = gs_ls(pattern, archive_url, single)
106    if not payload_uri_list:
107        return None if single else []
108
109    return payload_uri_list[0] if single else payload_uri_list
110
111
112def find_image_uri(archive_url):
113    """Returns a URI to a test image.
114
115    @param archive_url: archive_url directory to find the payload.
116
117    @return A URI to the desired image if found, None otherwise. It will most
118            likely be a file inside an image archive (image.zip), in which case
119            we'll be using ZIPFILE_BOUNDARY ('//') to denote a zip-encapsulated
120            file, for example:
121            gs://chromeos-image-archive/.../image.zip//chromiumos_test_image.bin
122
123    @raise TestImageError if an error has occurred.
124
125    """
126    image_archive = gs_ls('image.zip', archive_url, single=True)
127    if not image_archive:
128        return None
129
130    return (image_archive[0] + ZIPFILE_BOUNDARY + 'chromiumos_test_image.bin')
131