xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 1 | # Copyright 2017 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 ChromeOS & Android build related logic in suite scheduler.""" |
| 6 | |
| 7 | import apiclient |
| 8 | import collections |
| 9 | import logging |
| 10 | import re |
| 11 | |
| 12 | # Bare branches |
| 13 | BARE_BRANCHES = ['factory', 'firmware'] |
| 14 | |
| 15 | # Definition of os types. |
| 16 | OS_TYPE_CROS = 'cros' |
| 17 | OS_TYPE_BRILLO = 'brillo' |
| 18 | OS_TYPE_ANDROID = 'android' |
| 19 | OS_TYPES = [OS_TYPE_CROS, OS_TYPE_BRILLO, OS_TYPE_ANDROID] |
| 20 | OS_TYPES_LAUNCH_CONTROL = [OS_TYPE_BRILLO, OS_TYPE_ANDROID] |
| 21 | |
| 22 | |
| 23 | LaunchControlBuildTargetInfo = collections.namedtuple( |
| 24 | 'LaunchControlBuildTargetInfo', |
| 25 | [ |
| 26 | 'target', |
| 27 | 'type', |
| 28 | ]) |
| 29 | |
| 30 | CrOSBuildConfigInfo = collections.namedtuple( |
| 31 | 'CrOSBuildConfigInfo', |
| 32 | [ |
| 33 | 'board', |
| 34 | 'type', |
| 35 | ]) |
| 36 | |
| 37 | |
| 38 | class NoBuildError(Exception): |
| 39 | """Raised when failing to get the required build from Google Storage.""" |
| 40 | |
| 41 | |
| 42 | class BuildType(object): |
| 43 | """Representing the type of test source build. |
| 44 | |
| 45 | This is used to identify the test source build for testing. |
| 46 | """ |
| 47 | FIRMWARE_RW = 'firmware_rw' |
| 48 | FIRMWARE_RO = 'firmware_ro' |
| 49 | CROS = 'cros' |
| 50 | |
| 51 | |
| 52 | class BuildVersionKey(object): |
| 53 | """Keys referring to the builds to install in run_suites.""" |
| 54 | |
| 55 | CROS_VERSION = 'cros_version' |
| 56 | ANDROID_BUILD_VERSION = 'android_version' |
| 57 | TESTBED_BUILD_VERSION = 'testbed_version' |
| 58 | FW_RW_VERSION = 'fwrw_version' |
| 59 | FW_RO_VERSION = 'fwro_version' |
| 60 | |
| 61 | |
| 62 | class AndroidBuild(collections.namedtuple( |
| 63 | '_AndroidBuildBase', ['branch', 'target', 'build_id']), object): |
| 64 | |
| 65 | def __str__(self): |
| 66 | return _ANDROID_BUILD_PATTERN % {'branch': self.branch, |
| 67 | 'target': self.target, |
| 68 | 'build_id': self.build_id} |
| 69 | |
| 70 | |
| 71 | class CrOSBuild(collections.namedtuple( |
| 72 | '_CrOSBuildBase', |
| 73 | ['board', 'build_type', 'milestone', 'manifest']), object): |
| 74 | |
| 75 | def __str__(self): |
| 76 | return _CROS_BUILD_PATTERN % {'board': self.board, |
| 77 | 'build_type': self.build_type, |
| 78 | 'milestone': self.milestone, |
| 79 | 'manifest': self.manifest} |
| 80 | |
| 81 | |
| 82 | def get_latest_cros_build_from_gs(storage_client, board=None, suffix=None): |
| 83 | """Get latest build for given board from Google Storage. |
| 84 | |
| 85 | Args: |
| 86 | storage_client: a rest_client.StorageRestClient object. |
| 87 | board: the board to fetch latest build. Default is 'master'. |
| 88 | suffix: suffix represents build channel, like '-release'. |
| 89 | Default is '-paladin'. |
| 90 | |
| 91 | Returns: |
| 92 | a ChromeOS version string, e.g. '59.0.000.0'. |
| 93 | |
| 94 | Raises: |
| 95 | HttpError if error happens in interacting with Google Storage. |
| 96 | """ |
| 97 | board = board if board is not None else _DEFAULT_MASTER |
| 98 | suffix = suffix if suffix is not None else _DEFAULT_BUILD_SUFFIX |
| 99 | file_to_check = _GS_LATEST_MASTER_PATTERN % { |
| 100 | 'board': board, |
| 101 | 'suffix': suffix, |
| 102 | 'name': _LATEST_MASTER} |
| 103 | |
| 104 | try: |
| 105 | return storage_client.ReadObject(_GS_BUCKET, file_to_check) |
| 106 | except apiclient.errors.HttpError as e: |
| 107 | raise NoBuildError( |
| 108 | 'Cannot find latest build for board %s, suffix %s: %s' % |
| 109 | (board, suffix, str(e))) |
| 110 | |
| 111 | |
| 112 | def get_latest_launch_control_build(android_client, branch, target): |
| 113 | """Get the latest launch control build from Android Build API. |
| 114 | |
| 115 | Args: |
| 116 | android_client: a rest_client.AndroidBuildRestClient object. |
| 117 | branch: the launch control branch. |
| 118 | target: the launch control target. |
| 119 | |
| 120 | Returns: |
| 121 | a string latest launch control build id. |
| 122 | |
| 123 | Raises: |
| 124 | NoBuildError if no latest launch control build is found. |
| 125 | """ |
| 126 | try: |
| 127 | latest_build_id = android_client.get_latest_build_id(branch, target) |
| 128 | if latest_build_id is None: |
| 129 | raise NoBuildError('No latest builds is found.') |
| 130 | |
| 131 | return latest_build_id |
| 132 | except apiclient.errors.HttpError as e: |
| 133 | raise NoBuildError('HttpError happened in getting the latest launch ' |
| 134 | 'control build for ' |
| 135 | '%s,%s: %s' % (branch, target, str(e))) |
| 136 | |
| 137 | def parse_launch_control_target(target): |
| 138 | """Parse the build target and type from a Launch Control target. |
| 139 | |
| 140 | The Launch Control target has the format of build_target-build_type, e.g., |
| 141 | shamu-eng or dragonboard-userdebug. This method extracts the build target |
| 142 | and type from the target name. |
| 143 | |
| 144 | Args: |
| 145 | target: Name of a Launch Control target, e.g., shamu-userdebug. |
| 146 | |
| 147 | Returns: |
| 148 | a LaunchControlBuildTargetInfo object whose value is like |
| 149 | (target='shamu', |
| 150 | type='userdebug') |
| 151 | |
| 152 | Raises: |
| 153 | Raise ValueError if target is not valid. |
| 154 | """ |
| 155 | match = re.match(_LAUNCH_CONTROL_TARGET_PATTERN, target) |
| 156 | if not match: |
| 157 | raise ValueError('target format is not valid') |
| 158 | |
| 159 | return LaunchControlBuildTargetInfo(match.group('build_target'), |
| 160 | match.group('build_type')) |
| 161 | |
| 162 | |
| 163 | def parse_cros_build_config(board, build_config): |
| 164 | """Parse build_type from a given builder for a given board. |
| 165 | |
| 166 | Args: |
| 167 | board: the prefix of a ChromeOS build_config, representing board. |
| 168 | build_config: a ChromeOS build_config name, like 'kevin-release'. |
| 169 | |
| 170 | Returns: |
| 171 | a CrOSBuildConfigInfo object whose value is like |
| 172 | (board='kevin', |
| 173 | type='release') |
| 174 | |
| 175 | Raises: |
| 176 | Raise ValueError if target is not valid. |
| 177 | """ |
| 178 | if build_config[0:len(board)] != board: |
| 179 | raise ValueError('build_config cannot be parsed: %s' % build_config) |
| 180 | |
| 181 | match = re.match(_CROS_BUILD_CONFIG_PATTERN, build_config[len(board):]) |
| 182 | if not match: |
| 183 | raise ValueError('build_config %s is not matched %s' % ( |
| 184 | build_config, _CROS_BUILD_CONFIG_PATTERN)) |
| 185 | |
| 186 | return CrOSBuildConfigInfo(board, match.groups()[0]) |
| 187 | |
| 188 | |
| 189 | def get_board_by_android_target(target): |
| 190 | """Map a android target to a android board. |
| 191 | |
| 192 | # Mapping between an android board name and a build target. This is for |
| 193 | # special case handling for certain Android board that the board name and |
| 194 | # build target name does not match. |
| 195 | # This comes from server/site_utils.py in autotest module. |
| 196 | |
| 197 | Args: |
| 198 | board: an android board. |
| 199 | |
| 200 | Returns: |
| 201 | a string android board mapped by ANDROID_TARGET_TO_BOARD_MAP. |
| 202 | """ |
| 203 | return _ANDROID_TARGET_TO_BOARD_MAP.get(target, target) |
| 204 | |
| 205 | |
| 206 | # The default build type for fetching latest build. |
| 207 | _DEFAULT_BUILD_SUFFIX = '-paladin' |
| 208 | |
| 209 | # The default setting of board for fetching latest build. |
| 210 | _DEFAULT_MASTER = 'master' |
| 211 | |
| 212 | # The path for storing the latest build. |
| 213 | _GS_LATEST_MASTER_PATTERN = '%(board)s%(suffix)s/%(name)s' |
| 214 | |
| 215 | # The gs bucket to fetch the latest build. |
| 216 | _GS_BUCKET = 'chromeos-image-archive' |
| 217 | |
| 218 | # The file in Google Storage to fetch the latest build. |
| 219 | _LATEST_MASTER = 'LATEST-master' |
| 220 | |
| 221 | # Special android target to board map. |
| 222 | _ANDROID_TARGET_TO_BOARD_MAP = { |
| 223 | 'seed_l8150': 'gm4g_sprout', |
| 224 | 'bat_land': 'bat' |
| 225 | } |
| 226 | |
| 227 | # CrOS build name patter |
| 228 | _CROS_BUILD_PATTERN = '%(board)s-%(build_type)s/R%(milestone)s-%(manifest)s' |
| 229 | |
| 230 | # Android build name pattern |
| 231 | _ANDROID_BUILD_PATTERN = '%(branch)s/%(target)s/%(build_id)s' |
| 232 | |
| 233 | # The pattern for Launch Control target |
| 234 | _LAUNCH_CONTROL_TARGET_PATTERN = r'(?P<build_target>.+)-(?P<build_type>[^-]+)' |
| 235 | |
| 236 | # The pattern for CrOS build config |
| 237 | _CROS_BUILD_CONFIG_PATTERN = r'-([^-]+)(?:-group)?' |