Jack Neus | 56bd8c9 | 2022-09-21 23:05:34 +0000 | [diff] [blame] | 1 | # Copyright 2017 The ChromiumOS Authors |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 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.""" |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 6 | # pylint: disable=g-bad-import-order |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 7 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 8 | from distutils import version |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 9 | import collections |
Xixuan Wu | 244e0ec | 2018-05-23 14:49:55 -0700 | [diff] [blame] | 10 | import json |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 11 | import logging |
| 12 | import re |
| 13 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 14 | import apiclient |
| 15 | |
Xixuan Wu | f856ff1 | 2019-05-21 14:09:38 -0700 | [diff] [blame] | 16 | BuildInfo = collections.namedtuple( |
| 17 | 'BuildInfo', |
| 18 | [ |
| 19 | 'board', |
| 20 | 'model', |
| 21 | 'milestone', |
| 22 | 'platform', |
| 23 | 'build_config', |
| 24 | ]) |
| 25 | |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 26 | # Bare branches |
| 27 | BARE_BRANCHES = ['factory', 'firmware'] |
| 28 | |
| 29 | # Definition of os types. |
| 30 | OS_TYPE_CROS = 'cros' |
| 31 | OS_TYPE_BRILLO = 'brillo' |
| 32 | OS_TYPE_ANDROID = 'android' |
| 33 | OS_TYPES = [OS_TYPE_CROS, OS_TYPE_BRILLO, OS_TYPE_ANDROID] |
| 34 | OS_TYPES_LAUNCH_CONTROL = [OS_TYPE_BRILLO, OS_TYPE_ANDROID] |
| 35 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 36 | # Launch control build's target's information |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 37 | LaunchControlBuildTargetInfo = collections.namedtuple( |
| 38 | 'LaunchControlBuildTargetInfo', |
| 39 | [ |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 40 | 'target', |
| 41 | 'type', |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 42 | ]) |
| 43 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 44 | # ChromeOS build config's information |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 45 | CrOSBuildConfigInfo = collections.namedtuple( |
| 46 | 'CrOSBuildConfigInfo', |
| 47 | [ |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 48 | 'board', |
| 49 | 'type', |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 50 | ]) |
| 51 | |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 52 | # The default build type for fetching latest build. |
Xixuan Wu | 7899db5 | 2018-05-29 14:19:06 -0700 | [diff] [blame] | 53 | _DEFAULT_BUILD_SUFFIX = '-release' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 54 | |
| 55 | # The default setting of board for fetching latest build. |
Jack Neus | 56bd8c9 | 2022-09-21 23:05:34 +0000 | [diff] [blame] | 56 | _DEFAULT_HEAD = 'main' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 57 | |
| 58 | # The path for storing the latest build. |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 59 | _GS_LATEST_HEAD_PATTERN = '%(board)s%(suffix)s/%(name)s' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 60 | |
| 61 | # The gs bucket to fetch the latest build. |
| 62 | _GS_BUCKET = 'chromeos-image-archive' |
| 63 | |
| 64 | # The file in Google Storage to fetch the latest build. |
Dhanya Ganesh | 8860cf0 | 2021-04-13 21:12:12 +0000 | [diff] [blame] | 65 | _LATEST_HEAD = 'LATEST-main' |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 66 | |
Xixuan Wu | 244e0ec | 2018-05-23 14:49:55 -0700 | [diff] [blame] | 67 | # The bucket for ChromeOS build release boards. Maintained by GoldenEye. |
| 68 | _GE_RELEASE_BUILD_BUCKET = 'chromeos-build-release-console' |
| 69 | |
xixuan | bea010f | 2017-03-27 10:10:19 -0700 | [diff] [blame] | 70 | # Special android target to board map. |
| 71 | _ANDROID_TARGET_TO_BOARD_MAP = { |
| 72 | 'seed_l8150': 'gm4g_sprout', |
| 73 | 'bat_land': 'bat' |
| 74 | } |
| 75 | |
| 76 | # CrOS build name patter |
| 77 | _CROS_BUILD_PATTERN = '%(board)s-%(build_type)s/R%(milestone)s-%(manifest)s' |
| 78 | |
| 79 | # Android build name pattern |
| 80 | _ANDROID_BUILD_PATTERN = '%(branch)s/%(target)s/%(build_id)s' |
| 81 | |
| 82 | # The pattern for Launch Control target |
| 83 | _LAUNCH_CONTROL_TARGET_PATTERN = r'(?P<build_target>.+)-(?P<build_type>[^-]+)' |
| 84 | |
| 85 | # The pattern for CrOS build config |
| 86 | _CROS_BUILD_CONFIG_PATTERN = r'-([^-]+)(?:-group)?' |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 87 | |
| 88 | |
| 89 | class NoBuildError(Exception): |
| 90 | """Raised when failing to get the required build from Google Storage.""" |
| 91 | |
| 92 | |
| 93 | class BuildType(object): |
| 94 | """Representing the type of test source build. |
| 95 | |
| 96 | This is used to identify the test source build for testing. |
| 97 | """ |
| 98 | FIRMWARE_RW = 'firmware_rw' |
| 99 | FIRMWARE_RO = 'firmware_ro' |
| 100 | CROS = 'cros' |
| 101 | |
| 102 | |
| 103 | class BuildVersionKey(object): |
| 104 | """Keys referring to the builds to install in run_suites.""" |
| 105 | |
| 106 | CROS_VERSION = 'cros_version' |
| 107 | ANDROID_BUILD_VERSION = 'android_version' |
| 108 | TESTBED_BUILD_VERSION = 'testbed_version' |
| 109 | FW_RW_VERSION = 'fwrw_version' |
| 110 | FW_RO_VERSION = 'fwro_version' |
| 111 | |
| 112 | |
| 113 | class AndroidBuild(collections.namedtuple( |
| 114 | '_AndroidBuildBase', ['branch', 'target', 'build_id']), object): |
| 115 | """Class for constructing android build string.""" |
| 116 | |
| 117 | def __str__(self): |
| 118 | return _ANDROID_BUILD_PATTERN % {'branch': self.branch, |
| 119 | 'target': self.target, |
| 120 | 'build_id': self.build_id} |
| 121 | |
| 122 | |
| 123 | class CrOSBuild(collections.namedtuple( |
| 124 | '_CrOSBuildBase', |
| 125 | ['board', 'build_type', 'milestone', 'manifest']), object): |
| 126 | """Class for constructing ChromeOS build string.""" |
| 127 | |
| 128 | def __str__(self): |
| 129 | return _CROS_BUILD_PATTERN % {'board': self.board, |
| 130 | 'build_type': self.build_type, |
| 131 | 'milestone': self.milestone, |
| 132 | 'manifest': self.manifest} |
| 133 | |
| 134 | |
| 135 | def get_latest_cros_build_from_gs(storage_client, board=None, suffix=None): |
| 136 | """Get latest build for given board from Google Storage. |
| 137 | |
| 138 | Args: |
| 139 | storage_client: a rest_client.StorageRestClient object. |
Jack Neus | 56bd8c9 | 2022-09-21 23:05:34 +0000 | [diff] [blame] | 140 | board: the board to fetch latest build. Default is 'main'. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 141 | suffix: suffix represents build channel, like '-release'. |
| 142 | Default is '-paladin'. |
| 143 | |
| 144 | Returns: |
| 145 | a ChromeOS version string, e.g. '59.0.000.0'. |
| 146 | |
| 147 | Raises: |
| 148 | HttpError if error happens in interacting with Google Storage. |
| 149 | """ |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 150 | board = board if board is not None else _DEFAULT_HEAD |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 151 | suffix = suffix if suffix is not None else _DEFAULT_BUILD_SUFFIX |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 152 | file_to_check = _GS_LATEST_HEAD_PATTERN % { |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 153 | 'board': board, |
| 154 | 'suffix': suffix, |
Ned Nguyen | b0a377d | 2020-08-27 08:43:30 -0600 | [diff] [blame] | 155 | 'name': _LATEST_HEAD} |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 156 | |
| 157 | try: |
| 158 | return storage_client.read_object(_GS_BUCKET, file_to_check) |
| 159 | except apiclient.errors.HttpError as e: |
| 160 | raise NoBuildError( |
| 161 | 'Cannot find latest build for board %s, suffix %s: %s' % |
| 162 | (board, suffix, str(e))) |
| 163 | |
| 164 | |
Xixuan Wu | 244e0ec | 2018-05-23 14:49:55 -0700 | [diff] [blame] | 165 | def get_board_family_mapping_from_gs(storage_client): |
| 166 | """Get board_family to boards mapping from Google Storage. |
| 167 | |
| 168 | Args: |
| 169 | storage_client: a rest_client.StorageRestClient object. |
| 170 | |
| 171 | Returns: |
| 172 | a dictionary of mapping between board family name to boards, e.g. |
| 173 | {'nyan': ['nyan', 'nyan_big', 'nyan_blaze', ..]} |
| 174 | |
| 175 | Raises: |
| 176 | HttpError if error happens in interacting with Google Storage. |
| 177 | """ |
| 178 | try: |
| 179 | boards = storage_client.read_object(_GE_RELEASE_BUILD_BUCKET, 'boards.json') |
| 180 | json_object = json.loads(boards) |
| 181 | board_family = {} |
| 182 | for board in json_object['boards']: |
| 183 | group = board['reference_group'] |
| 184 | if not group: |
| 185 | continue |
| 186 | |
| 187 | # This is to change boards like nyan-blaze to nyan_blaze, which is |
| 188 | # actually used in lab. |
| 189 | board_name = board['public_codename'].replace('-', '_') |
| 190 | if group not in board_family: |
| 191 | board_family[group] = [] |
| 192 | |
| 193 | board_family[group].append(board_name) |
| 194 | |
| 195 | logging.info('Successfully get following board families from GS: %r', |
| 196 | board_family.keys()) |
| 197 | return board_family |
| 198 | except apiclient.errors.HttpError as e: |
| 199 | logging.error('Cannot load boards.json in bucket %s: %s', |
| 200 | _GE_RELEASE_BUILD_BUCKET, str(e)) |
| 201 | raise |
| 202 | |
| 203 | |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 204 | def buildinfo_list_to_branch_build_dict(cros_board_list, buildinfo_list, |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 205 | stable_rubik_milestones = None, |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 206 | use_rubik = False): |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 207 | """Validate and convert a list of BuildInfo to branch build dict. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 208 | |
| 209 | Args: |
Xixuan Wu | 6fb1627 | 2017-10-19 13:16:00 -0700 | [diff] [blame] | 210 | cros_board_list: The board list including all CrOS boards. |
Xixuan Wu | c681901 | 2019-05-23 11:34:59 -0700 | [diff] [blame] | 211 | buildinfo_list: A list of BuildInfo objects. |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 212 | stable_rubik_milestones: map of build target to stable Rubik milestone, |
| 213 | used to determine where we should use Rubik builds instead of Leagcy. |
| 214 | use_rubik: bool. If True will only keep builds for milestones >= the stable |
| 215 | rubik milestone for the build target (as specified in |
| 216 | stable_rubik_milestone) If False, will only keep builds for milestones < |
| 217 | the stable rubik milestone. If a build target is not included in |
| 218 | stable_rubik_milestones, no Rubik builds for that build target will be |
| 219 | used. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 220 | |
| 221 | Returns: |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 222 | A branch build dict: |
| 223 | key: a tuple of (board, build_type, milestone), like: |
| 224 | ('wolf', 'release', '58') |
| 225 | value: the latest manifest for the given tuple, like: |
| 226 | '9242.0.0'. |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 227 | """ |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 228 | branch_build_dict = {} |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 229 | for build in buildinfo_list: |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 230 | try: |
| 231 | build_config_info = parse_cros_build_config(build.board, |
| 232 | build.build_config) |
| 233 | except ValueError as e: |
| 234 | logging.warning('Failed to parse build config: %s: %s', |
| 235 | build.build_config, e) |
| 236 | continue |
| 237 | |
| 238 | if build.board != build_config_info.board: |
| 239 | logging.warning('Non-matched build_config and board: %s, %s', |
| 240 | build.board, build.board) |
| 241 | continue |
| 242 | |
Xixuan Wu | 6fb1627 | 2017-10-19 13:16:00 -0700 | [diff] [blame] | 243 | if build.board not in cros_board_list: |
| 244 | logging.warning('%s is not a valid CrOS board.', build.board) |
| 245 | continue |
| 246 | |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 247 | if stable_rubik_milestones: |
| 248 | stable_rubik_milestone = stable_rubik_milestones.get(build.board, None) |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 249 | if use_rubik: |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 250 | # Discard builds for milestones before the stable_rubik_milestone. |
| 251 | if not stable_rubik_milestone or int(build.milestone) < stable_rubik_milestone: |
| 252 | logging.warning('%s is not a Rubik board for milestone %s, discarding.', |
| 253 | build.board, build.milestone) |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 254 | continue |
| 255 | else: |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 256 | # Discard builds for milestones after the stable_rubik_milestone. |
| 257 | if stable_rubik_milestone and int(build.milestone) >= stable_rubik_milestones: |
| 258 | logging.warning('%s is a Rubik board for milestone %s, discarding.', |
| 259 | build.board, build.milestone) |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 260 | continue |
| 261 | |
Xixuan Wu | 8d2f286 | 2018-08-28 16:48:04 -0700 | [diff] [blame] | 262 | build_key = (build.board, build.model, build_config_info.type, |
| 263 | build.milestone) |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 264 | cur_manifest = branch_build_dict.get(build_key) |
| 265 | if cur_manifest is not None: |
| 266 | branch_build_dict[build_key] = max( |
| 267 | [cur_manifest, build.platform], key=version.LooseVersion) |
| 268 | else: |
| 269 | branch_build_dict[build_key] = build.platform |
| 270 | |
| 271 | return branch_build_dict |
| 272 | |
| 273 | |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 274 | def get_cros_builds(build_client, cros_board_list, earliest_end_time, |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 275 | latest_end_time, event_type, stable_rubik_milestones = None): |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 276 | """Get branch builds for ChromeOS boards inside the given time span. |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 277 | |
| 278 | Args: |
Xixuan Wu | c681901 | 2019-05-23 11:34:59 -0700 | [diff] [blame] | 279 | build_client: a rest_client.BuildBucketBigqueryClient object, to |
| 280 | connect Buildbucket Bigquery. |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 281 | cros_board_list: The board list including all CrOS boards. |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 282 | earliest_end_time: a datetime.datetime object in UTC. |
| 283 | latest_end_time: a datetime.datetime object in UTC. |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 284 | event: a string of event type. |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 285 | stable_rubik_milestones: map of build target to stable Rubik milestone, |
| 286 | used to determine where we should use Rubik builds instead of Leagcy. |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 287 | |
| 288 | Returns: |
| 289 | a two-tuple of branch build dicts. Each branch build dict has |
| 290 | keys and value described below. The first build_dict describes |
| 291 | successful builds and the second describes builds that failed |
Jared Loucks | 7068d8b | 2022-04-28 09:41:33 -0600 | [diff] [blame] | 292 | but met the relaxed success requirement (a successful 'UploadTestArtifacts' |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 293 | stage). |
| 294 | key: a tuple of (board, build_type, milestone), like: |
| 295 | ('wolf', 'release', '58') |
| 296 | value: the latest manifest for the given tuple, like: |
| 297 | '9242.0.0'. |
| 298 | """ |
Xixuan Wu | c681901 | 2019-05-23 11:34:59 -0700 | [diff] [blame] | 299 | # Buildbucket Bigquery use UTC timezone |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 300 | all_branch_builds = build_client.get_passed_builds( |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 301 | earliest_end_time, latest_end_time, event_type) |
Xinan Lin | ea1efcb | 2019-12-30 23:46:42 -0800 | [diff] [blame] | 302 | relaxed_builds = build_client.get_relaxed_passed_builds( |
Xinan Lin | 71eeeb0 | 2020-03-10 17:37:12 -0700 | [diff] [blame] | 303 | earliest_end_time, latest_end_time, event_type) |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 304 | all_branch_rubik_builds = build_client.get_passed_rubik_builds( |
| 305 | earliest_end_time, latest_end_time, event_type) |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 306 | |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 307 | # TODO(b/225382624): Remove all logic fetching Legacy builds |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 308 | branch_builds_dict = buildinfo_list_to_branch_build_dict( |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 309 | cros_board_list, all_branch_builds, stable_rubik_milestones=stable_rubik_milestones) |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 310 | rubik_branch_builds_dict = buildinfo_list_to_branch_build_dict( |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 311 | cros_board_list, all_branch_rubik_builds, stable_rubik_milestones=stable_rubik_milestones, |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 312 | use_rubik=True) |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 313 | relaxed_builds_dict = buildinfo_list_to_branch_build_dict( |
Jack Neus | cb14a3b | 2022-04-01 16:39:38 +0000 | [diff] [blame] | 314 | cros_board_list, relaxed_builds + all_branch_builds, |
Jack Neus | 2cbdc43 | 2022-06-24 20:21:30 +0000 | [diff] [blame] | 315 | stable_rubik_milestones=stable_rubik_milestones) |
Jack Neus | cb14a3b | 2022-04-01 16:39:38 +0000 | [diff] [blame] | 316 | # Include Rubik builds in the relaxed builds dict. We can't pass the rubik |
| 317 | # builds to the above call because they'll get filtered out (use_rubik is False). |
| 318 | relaxed_builds_dict.update(rubik_branch_builds_dict) |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 319 | |
Jack Neus | 8f0edb4 | 2022-03-17 20:21:39 +0000 | [diff] [blame] | 320 | return dict(branch_builds_dict, **rubik_branch_builds_dict), relaxed_builds_dict |
Craig Bergstrom | 58263d3 | 2018-04-26 14:11:35 -0600 | [diff] [blame] | 321 | |
| 322 | |
Xixuan Wu | 5d6063e | 2017-09-05 16:15:07 -0700 | [diff] [blame] | 323 | def get_latest_launch_control_build(android_client, branch, target): |
| 324 | """Get the latest launch control build from Android Build API. |
| 325 | |
| 326 | Args: |
| 327 | android_client: a rest_client.AndroidBuildRestClient object. |
| 328 | branch: the launch control branch. |
| 329 | target: the launch control target. |
| 330 | |
| 331 | Returns: |
| 332 | a string latest launch control build id. |
| 333 | |
| 334 | Raises: |
| 335 | NoBuildError if no latest launch control build is found. |
| 336 | """ |
| 337 | try: |
| 338 | latest_build_id = android_client.get_latest_build_id(branch, target) |
| 339 | if latest_build_id is None: |
| 340 | raise NoBuildError('No latest builds is found.') |
| 341 | |
| 342 | return latest_build_id |
| 343 | except apiclient.errors.HttpError as e: |
| 344 | raise NoBuildError('HttpError happened in getting the latest launch ' |
| 345 | 'control build for ' |
| 346 | '%s,%s: %s' % (branch, target, str(e))) |
| 347 | |
| 348 | |
| 349 | def get_launch_control_builds_by_branch_targets( |
| 350 | android_client, android_board_list, launch_control_branch_targets): |
| 351 | """Get latest launch_control_builds for android boards. |
| 352 | |
| 353 | For every tasks in this event, if it has settings of launch control |
| 354 | branch & target, get the latest launch control build for it. |
| 355 | |
| 356 | Args: |
| 357 | android_client: a rest_client.AndroidBuildRestClient object to |
| 358 | interact with android build API. |
| 359 | android_board_list: a list of Android boards. |
| 360 | launch_control_branch_targets: a dict of branch:targets, see property |
| 361 | launch_control_branch_targets in base_event.py. |
| 362 | |
| 363 | Returns: |
| 364 | a launch control build dict: |
| 365 | key: an android board, like 'shamu'. |
| 366 | value: a list involves the latest builds for each pair |
| 367 | (branch, target) of this board, like: |
| 368 | [u'git_nyc-mr2-release/shamu-userdebug/3844975', |
| 369 | u'git_nyc-mr1-release/shamu-userdebug/3783920'] |
| 370 | """ |
| 371 | launch_control_dict = {} |
| 372 | board_to_builds_dict = {} |
| 373 | for branch, targets in launch_control_branch_targets.iteritems(): |
| 374 | for t in targets: |
| 375 | try: |
| 376 | board = parse_launch_control_target(t).target |
| 377 | except ValueError: |
| 378 | logging.warning( |
| 379 | 'Failed to parse launch control target: %s', t) |
| 380 | continue |
| 381 | |
| 382 | if board not in android_board_list: |
| 383 | continue |
| 384 | |
| 385 | # Use dict here to reduce the times to call AndroidBuild API |
| 386 | if launch_control_dict.get((branch, t)) is None: |
| 387 | try: |
| 388 | build_id = get_latest_launch_control_build( |
| 389 | android_client, branch, t) |
| 390 | except NoBuildError as e: |
| 391 | logging.warning(e) |
| 392 | continue |
| 393 | |
| 394 | build = str(AndroidBuild(branch, t, build_id)) |
| 395 | launch_control_dict[(branch, t)] = build |
| 396 | board_to_builds_dict.setdefault(board, []).append( |
| 397 | build) |
| 398 | |
| 399 | for board, in board_to_builds_dict.iteritems(): |
| 400 | mapped_board = get_board_by_android_target(board) |
| 401 | if mapped_board != board: |
| 402 | logging.debug('Map board %s to %s', board, mapped_board) |
| 403 | if board_to_builds_dict.get(mapped_board) is None: |
| 404 | del board_to_builds_dict[board] |
| 405 | else: |
| 406 | board_to_builds_dict[board] = board_to_builds_dict[ |
| 407 | mapped_board] |
| 408 | |
| 409 | return board_to_builds_dict |
| 410 | |
| 411 | |
| 412 | def parse_launch_control_target(target): |
| 413 | """Parse the build target and type from a Launch Control target. |
| 414 | |
| 415 | The Launch Control target has the format of build_target-build_type, e.g., |
| 416 | shamu-eng or dragonboard-userdebug. This method extracts the build target |
| 417 | and type from the target name. |
| 418 | |
| 419 | Args: |
| 420 | target: Name of a Launch Control target, e.g., shamu-userdebug. |
| 421 | |
| 422 | Returns: |
| 423 | a LaunchControlBuildTargetInfo object whose value is like |
| 424 | (target='shamu', |
| 425 | type='userdebug') |
| 426 | |
| 427 | Raises: |
| 428 | ValueError: if target is not valid. |
| 429 | """ |
| 430 | match = re.match(_LAUNCH_CONTROL_TARGET_PATTERN, target) |
| 431 | if not match: |
| 432 | raise ValueError('target format is not valid') |
| 433 | |
| 434 | return LaunchControlBuildTargetInfo(match.group('build_target'), |
| 435 | match.group('build_type')) |
| 436 | |
| 437 | |
| 438 | def parse_cros_build_config(board, build_config): |
| 439 | """Parse build_type from a given builder for a given board. |
| 440 | |
| 441 | Args: |
| 442 | board: the prefix of a ChromeOS build_config, representing board. |
| 443 | build_config: a ChromeOS build_config name, like 'kevin-release'. |
| 444 | |
| 445 | Returns: |
| 446 | a CrOSBuildConfigInfo object whose value is like |
| 447 | (board='kevin', |
| 448 | type='release') |
| 449 | |
| 450 | Raises: |
| 451 | ValueError: if build_config is in invalid form. |
| 452 | """ |
| 453 | if build_config[0:len(board)] != board: |
| 454 | raise ValueError('build_config cannot be parsed: %s' % build_config) |
| 455 | |
| 456 | match = re.match(_CROS_BUILD_CONFIG_PATTERN, build_config[len(board):]) |
| 457 | if not match: |
| 458 | raise ValueError('build_config %s is not matched %s' % ( |
| 459 | build_config, _CROS_BUILD_CONFIG_PATTERN)) |
| 460 | |
| 461 | return CrOSBuildConfigInfo(board, match.groups()[0]) |
| 462 | |
| 463 | |
| 464 | def get_board_by_android_target(target): |
| 465 | """Map a android target to a android board. |
| 466 | |
| 467 | # Mapping between an android board name and a build target. This is for |
| 468 | # special case handling for certain Android board that the board name and |
| 469 | # build target name does not match. |
| 470 | # This comes from server/site_utils.py in autotest module. |
| 471 | |
| 472 | Args: |
| 473 | target: an android target. |
| 474 | |
| 475 | Returns: |
| 476 | a string android board mapped by ANDROID_TARGET_TO_BOARD_MAP. |
| 477 | """ |
| 478 | return _ANDROID_TARGET_TO_BOARD_MAP.get(target, target) |
Xixuan Wu | 588668d | 2019-01-04 10:33:31 -0800 | [diff] [blame] | 479 | |
| 480 | |
| 481 | def to_full_model(board, model): |
| 482 | """Form a full model name with given board, model. |
| 483 | |
| 484 | Args: |
| 485 | board: A string board name. |
| 486 | model: A string model name. |
| 487 | |
| 488 | Returns: |
| 489 | A string full_model name. |
| 490 | """ |
| 491 | return '%s_%s' % (board, model) |
Xixuan Wu | b4b2f41 | 2019-05-03 11:22:31 -0700 | [diff] [blame] | 492 | |
| 493 | |
| 494 | def reshape_board(board): |
| 495 | """Reshape some variants of board names to original board. |
| 496 | |
| 497 | Args: |
| 498 | board: A string board name. |
| 499 | |
| 500 | Returns: |
| 501 | A string board name after reshaping. |
| 502 | """ |
| 503 | # The hack derives from build info structure saved in CIDB/buildbucket: |
Kazuhiro Inaba | f020822 | 2020-03-05 10:33:07 +0900 | [diff] [blame] | 504 | # *-kernelnext/*-arcnext etc are saved as board name, but not the real board |
Xixuan Wu | b4b2f41 | 2019-05-03 11:22:31 -0700 | [diff] [blame] | 505 | # for DUT. |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 506 | # NOTE: please try to keep this list in sync with the corresponding list in |
| 507 | # chromite/lib/config_lib.py |
| 508 | special_suffixes = [ |
Navil Perez | c7a2917 | 2021-03-09 22:06:25 +0000 | [diff] [blame] | 509 | '-arc64', # |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 510 | '-arc-r', # |
| 511 | '-arc-r-userdebug', # |
Jiyoun Ha | ff42b7d | 2021-04-22 13:35:33 +0900 | [diff] [blame] | 512 | '-arc-s', # |
Kazuhiro Inaba | c05184e | 2022-05-11 16:52:55 +0900 | [diff] [blame] | 513 | '-arc-t', # |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 514 | '-arcnext', # |
| 515 | '-arcvm', # |
Arowa Suliman | 4c5b2bc | 2021-08-09 18:57:06 -0700 | [diff] [blame] | 516 | '-connectivitynext', # |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 517 | '-borealis', # |
| 518 | '-campfire', # |
| 519 | '-cfm', # |
| 520 | '-kernelnext', # |
| 521 | '-kvm', # |
Sean Abraham | d6815ba | 2021-02-26 09:29:53 -0700 | [diff] [blame] | 522 | '-libcamera', # |
C Shapiro | 975a83a | 2021-08-16 15:14:50 -0500 | [diff] [blame] | 523 | '-manatee', # |
Tomasz Nowicki | f7f974d | 2023-02-07 21:21:37 +0000 | [diff] [blame^] | 524 | '-manatee-kernelnext', # |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 525 | '-ndktranslation', # |
Kazuhiro Inaba | 840dac7 | 2021-02-01 16:47:05 +0900 | [diff] [blame] | 526 | '-userdebug', # |
Sean Abraham | bf297a9 | 2020-12-10 09:19:49 -0700 | [diff] [blame] | 527 | ] |
Stephen Boyd | fb1bfaa | 2021-07-14 21:19:48 +0000 | [diff] [blame] | 528 | # ARM64 userspace boards use 64 suffix but can't put that in list above |
| 529 | # because of collisions with boards like kevin-arc64. |
Stephen Boyd | e8446cc | 2022-10-21 12:21:16 -0700 | [diff] [blame] | 530 | ARM64_BOARDS = ['kevin64', 'strongbad64', 'trogdor64', 'cherry64', 'corsola64', 'jacuzzi64'] |
Sean Abraham | 2a177d7 | 2021-02-01 09:38:16 -0700 | [diff] [blame] | 531 | for s in special_suffixes: |
Sean Abraham | bf297a9 | 2020-12-10 09:19:49 -0700 | [diff] [blame] | 532 | if board.endswith(s): |
| 533 | board = board[:-len(s)] |
Stephen Boyd | fb1bfaa | 2021-07-14 21:19:48 +0000 | [diff] [blame] | 534 | if board in ARM64_BOARDS: |
| 535 | # Remove '64' suffix from the board name. |
| 536 | board = board[:-2] |
Xixuan Wu | b4b2f41 | 2019-05-03 11:22:31 -0700 | [diff] [blame] | 537 | return board |