1#!/usr/bin/env python 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16r"""AVDSpec class. 17 18AVDSpec will take in args from the user and be the main data type that will 19get passed into the create classes. The inferring magic will happen within 20initialization of AVDSpec (like LKGB build id, image branch, etc). 21""" 22 23import glob 24import logging 25import os 26import re 27import subprocess 28import tempfile 29import threading 30 31from acloud import errors 32from acloud.create import create_common 33from acloud.internal import constants 34from acloud.internal.lib import android_build_client 35from acloud.internal.lib import auth 36from acloud.internal.lib import utils 37from acloud.list import list as list_instance 38from acloud.public import config 39 40 41logger = logging.getLogger(__name__) 42 43# Default values for build target. 44_BRANCH_RE = re.compile(r"^Manifest branch: (?P<branch>.+)") 45_COMMAND_REPO_INFO = "repo info platform/tools/acloud" 46_REPO_TIMEOUT = 3 47_CF_ZIP_PATTERN = "*img*.zip" 48_DEFAULT_BUILD_BITNESS = "x86_64" 49_DEFAULT_BUILD_TYPE = "userdebug" 50_ENV_ANDROID_PRODUCT_OUT = "ANDROID_PRODUCT_OUT" 51_ENV_ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP" 52_GCE_LOCAL_IMAGE_CANDIDATES = ["avd-system.tar.gz", 53 "android_system_disk_syslinux.img"] 54_LOCAL_ZIP_WARNING_MSG = "'adb sync' will take a long time if using images " \ 55 "built with `m dist`. Building with just `m` will " \ 56 "enable a faster 'adb sync' process." 57_RE_ANSI_ESCAPE = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") 58_RE_FLAVOR = re.compile(r"^.+_(?P<flavor>.+)-img.+") 59_RE_MEMORY = re.compile(r"(?P<gb_size>\d+)g$|(?P<mb_size>\d+)m$", 60 re.IGNORECASE) 61_RE_INT = re.compile(r"^\d+$") 62_RE_RES = re.compile(r"^(?P<x_res>\d+)x(?P<y_res>\d+)$") 63_X_RES = "x_res" 64_Y_RES = "y_res" 65_COMMAND_GIT_REMOTE = ["git", "remote"] 66 67# The branch prefix is necessary for the Android Build system to know what we're 68# talking about. For instance, on an aosp remote repo in the master branch, 69# Android Build will recognize it as aosp-master. 70_BRANCH_PREFIX = {"aosp": "aosp-"} 71_DEFAULT_BRANCH_PREFIX = "git_" 72_DEFAULT_BRANCH = "aosp-master" 73 74# The target prefix is needed to help concoct the lunch target name given a 75# the branch, avd type and device flavor: 76# aosp, cf and phone -> aosp_cf_x86_phone. 77_BRANCH_TARGET_PREFIX = {"aosp": "aosp_"} 78_BRANCH_TARGET_TRUNK_STAGEING = {"aosp-main": "-trunk_staging", 79 "git_main": "-trunk_staging"} 80 81 82def EscapeAnsi(line): 83 """Remove ANSI control sequences (e.g. temrinal color codes...) 84 85 Args: 86 line: String, one line of command output. 87 88 Returns: 89 String without ANSI code. 90 """ 91 return _RE_ANSI_ESCAPE.sub('', line) 92 93 94# pylint: disable=too-many-public-methods,too-many-lines,too-many-statements 95class AVDSpec(): 96 """Class to store data on the type of AVD to create.""" 97 98 def __init__(self, args): 99 """Process the args into class vars. 100 101 Args: 102 args: Namespace object from argparse.parse_args. 103 """ 104 # Let's define the private class vars here and then process the user 105 # args afterwards. 106 self._client_adb_port = args.adb_port 107 self._autoconnect = None 108 self._cvd_host_package = None 109 self._instance_name_to_reuse = None 110 self._unlock_screen = None 111 self._report_internal_ip = None 112 self._disable_external_ip = None 113 self._extra_files = None 114 self._avd_type = None 115 self._flavor = None 116 self._force_sync = None 117 self._image_source = None 118 self._instance_type = None 119 self._launch_args = None 120 self._local_image_dir = None 121 self._local_image_artifact = None 122 self._local_instance_dir = None 123 self._local_kernel_image = None 124 self._local_system_image = None 125 self._local_system_dlkm_image = None 126 self._local_vendor_image = None 127 self._local_vendor_boot_image = None 128 self._local_tool_dirs = None 129 self._image_download_dir = None 130 self._num_of_instances = None 131 self._num_avds_per_instance = None 132 self._no_pull_log = None 133 self._mkcert = None 134 self._oxygen = None 135 self._openwrt = None 136 self._remote_image = {} 137 self._system_build_info = {} 138 self._kernel_build_info = {} 139 self._boot_build_info = {} 140 self._ota_build_info = {} 141 self._host_package_build_info = {} 142 self._bootloader_build_info = {} 143 self._android_efi_loader_build_info = {} 144 self._hw_property = None 145 self._hw_customize = False 146 self._remote_host = None 147 self._remote_image_dir = None 148 self._gce_metadata = None 149 self._gce_only = None 150 self._host_user = None 151 self._host_ssh_private_key_path = None 152 self._gpu = None 153 self._disk_type = None 154 self._base_instance_num = None 155 self._stable_host_image_name = None 156 self._use_launch_cvd = None 157 self._remote_fetch = None 158 self._webrtc_device_id = None 159 self._connect_hostname = None 160 self._fetch_cvd_wrapper = None 161 self._fetch_cvd_version = None 162 163 # Create config instance for android_build_client to query build api. 164 self._cfg = config.GetAcloudConfig(args) 165 # Reporting args. 166 self._serial_log_file = None 167 # emulator_* are only used for goldfish avd_type. 168 self._emulator_build_id = None 169 self._emulator_build_target = None 170 self._emulator_zip = None 171 172 # Fields only used for cheeps type. 173 self._stable_cheeps_host_image_name = None 174 self._stable_cheeps_host_image_project = None 175 self._username = None 176 self._password = None 177 self._cheeps_betty_image = None 178 self._cheeps_features = None 179 180 # The maximum time in seconds used to wait for the AVD to boot. 181 self._boot_timeout_secs = None 182 # The maximum time in seconds used to wait for the instance ready. 183 self._ins_timeout_secs = None 184 185 # The local instance id 186 self._local_instance_id = None 187 188 self._ProcessArgs(args) 189 190 def __repr__(self): 191 """Let's make it easy to see what this class is holding.""" 192 # TODO: I'm pretty sure there's a better way to do this, but I'm not 193 # quite sure what that would be. 194 representation = [] 195 representation.append("") 196 representation.append(" - instance_type: %s" % self._instance_type) 197 representation.append(" - avd type: %s" % self._avd_type) 198 representation.append(" - flavor: %s" % self._flavor) 199 representation.append(" - autoconnect: %s" % self._autoconnect) 200 representation.append(" - num of instances requested: %s" % 201 self._num_of_instances) 202 representation.append(" - image source type: %s" % 203 self._image_source) 204 image_summary = None 205 image_details = None 206 if self._image_source == constants.IMAGE_SRC_LOCAL: 207 image_summary = "local image dir" 208 image_details = self._local_image_dir 209 representation.append(" - instance id: %s" % self._local_instance_id) 210 elif self._image_source == constants.IMAGE_SRC_REMOTE: 211 image_summary = "remote image details" 212 image_details = self._remote_image 213 representation.append(" - %s: %s" % (image_summary, image_details)) 214 representation.append(" - hw properties: %s" % 215 self._hw_property) 216 return "\n".join(representation) 217 218 def _ProcessArgs(self, args): 219 """Main entry point to process args for the different type of args. 220 221 Split up the arg processing into related areas (image, instance type, 222 etc) so that we don't have one huge monolilthic method that does 223 everything. It makes it easier to review, write tests, and maintain. 224 225 Args: 226 args: Namespace object from argparse.parse_args. 227 """ 228 self._ProcessMiscArgs(args) 229 self._ProcessImageArgs(args) 230 self._ProcessHWPropertyArgs(args) 231 self._ProcessAutoconnect() 232 233 def _ProcessAutoconnect(self): 234 """Process autoconnect. 235 236 Only Cuttlefish AVD support 'webrtc' and need to default use 'webrtc'. 237 Other AVD types(goldfish, cheeps..etc.) still keep using ‘vnc’. 238 """ 239 if self._autoconnect == constants.INS_KEY_WEBRTC: 240 if self.avd_type != constants.TYPE_CF: 241 self._autoconnect = constants.INS_KEY_VNC 242 243 def _ProcessImageArgs(self, args): 244 """ Process Image Args. 245 246 Args: 247 args: Namespace object from argparse.parse_args. 248 """ 249 # If user didn't specify --local-image, infer remote image args 250 if args.local_image is None: 251 self._image_source = constants.IMAGE_SRC_REMOTE 252 self._ProcessRemoteBuildArgs(args) 253 else: 254 self._image_source = constants.IMAGE_SRC_LOCAL 255 self._ProcessLocalImageArgs(args) 256 257 if args.local_kernel_image is not None: 258 self._local_kernel_image = self._GetLocalImagePath( 259 args.local_kernel_image) 260 261 if args.local_system_image is not None: 262 self._local_system_image = self._GetLocalImagePath( 263 args.local_system_image) 264 265 if args.local_system_dlkm_image is not None: 266 self._local_system_dlkm_image = self._GetLocalImagePath( 267 args.local_system_dlkm_image) 268 269 if args.local_vendor_image is not None: 270 self._local_vendor_image = self._GetLocalImagePath( 271 args.local_vendor_image) 272 273 if args.local_vendor_boot_image is not None: 274 self._local_vendor_boot_image = self._GetLocalImagePath( 275 args.local_vendor_boot_image) 276 277 self.image_download_dir = ( 278 args.image_download_dir if args.image_download_dir 279 else tempfile.gettempdir()) 280 281 @staticmethod 282 def _ParseHWPropertyStr(hw_property_str): 283 """Parse string to dict. 284 285 Args: 286 hw_property_str: A hw properties string. 287 288 Returns: 289 Dict converted from a string. 290 291 Raises: 292 error.MalformedHWPropertyError: If hw_property_str is malformed. 293 """ 294 hw_dict = create_common.ParseKeyValuePairArgs(hw_property_str) 295 arg_hw_properties = {} 296 for key, value in hw_dict.items(): 297 # Parsing HW properties int to avdspec. 298 if key == constants.HW_ALIAS_RESOLUTION: 299 match = _RE_RES.match(value) 300 if match: 301 arg_hw_properties[_X_RES] = match.group("x_res") 302 arg_hw_properties[_Y_RES] = match.group("y_res") 303 else: 304 raise errors.InvalidHWPropertyError( 305 "[%s] is an invalid resolution. Example:1280x800" % value) 306 elif key in [constants.HW_ALIAS_MEMORY, constants.HW_ALIAS_DISK]: 307 match = _RE_MEMORY.match(value) 308 if match and match.group("gb_size"): 309 arg_hw_properties[key] = str( 310 int(match.group("gb_size")) * 1024) 311 elif match and match.group("mb_size"): 312 arg_hw_properties[key] = match.group("mb_size") 313 else: 314 raise errors.InvalidHWPropertyError( 315 "Expected gb size.[%s] is not allowed. Example:4g" % value) 316 elif key in [constants.HW_ALIAS_CPUS, constants.HW_ALIAS_DPI]: 317 if not _RE_INT.match(value): 318 raise errors.InvalidHWPropertyError( 319 "%s value [%s] is not an integer." % (key, value)) 320 arg_hw_properties[key] = value 321 322 return arg_hw_properties 323 324 def _ProcessHWPropertyArgs(self, args): 325 """Get the HW properties from argparse.parse_args. 326 327 This method will initialize _hw_property in the following 328 manner: 329 1. Get default hw properties from flavor. 330 2. Override hw properties from config. 331 3. Override by hw_property args. 332 333 Args: 334 args: Namespace object from argparse.parse_args. 335 """ 336 self._hw_property = {} 337 default_property = self._cfg.GetDefaultHwProperty(self._flavor, 338 self._instance_type) 339 self._hw_property = self._ParseHWPropertyStr(default_property) 340 logger.debug("Default hw property for [%s] flavor: %s", self._flavor, 341 self._hw_property) 342 if self._cfg.hw_property: 343 self._hw_customize = True 344 cfg_hw_property = self._ParseHWPropertyStr(self._cfg.hw_property) 345 logger.debug("Hw property from config: %s", cfg_hw_property) 346 self._hw_property.update(cfg_hw_property) 347 348 if args.hw_property: 349 self._hw_customize = True 350 arg_hw_property = self._ParseHWPropertyStr(args.hw_property) 351 logger.debug("Use custom hw property: %s", arg_hw_property) 352 self._hw_property.update(arg_hw_property) 353 354 def _ProcessMiscArgs(self, args): 355 """These args we can take as and don't belong to a group of args. 356 357 Args: 358 args: Namespace object from argparse.parse_args. 359 """ 360 self._autoconnect = args.autoconnect 361 self._unlock_screen = args.unlock_screen 362 self._report_internal_ip = args.report_internal_ip 363 self._disable_external_ip = args.disable_external_ip 364 self._avd_type = args.avd_type 365 self._extra_files = create_common.ParseExtraFilesArgs(args.extra_files) 366 self._flavor = args.flavor or constants.FLAVOR_PHONE 367 self._force_sync = args.force_sync 368 if args.remote_host: 369 self._instance_type = constants.INSTANCE_TYPE_HOST 370 else: 371 self._instance_type = (constants.INSTANCE_TYPE_REMOTE 372 if args.local_instance is None else 373 constants.INSTANCE_TYPE_LOCAL) 374 self._remote_host = args.remote_host 375 self._remote_image_dir = args.remote_image_dir 376 self._host_user = args.host_user 377 self._host_ssh_private_key_path = args.host_ssh_private_key_path 378 self._local_instance_id = args.local_instance 379 self._local_instance_dir = args.local_instance_dir 380 self._local_tool_dirs = args.local_tool 381 self._cvd_host_package = args.cvd_host_package 382 self._num_of_instances = args.num 383 self._num_avds_per_instance = args.num_avds_per_instance 384 self._no_pull_log = args.no_pull_log 385 self._mkcert = args.mkcert 386 self._oxygen = args.oxygen 387 self._openwrt = args.openwrt 388 self._use_launch_cvd = args.use_launch_cvd 389 self._serial_log_file = args.serial_log_file 390 self._emulator_build_id = args.emulator_build_id 391 self._emulator_build_target = (args.emulator_build_target 392 or self._cfg.emulator_build_target) 393 self._emulator_zip = args.emulator_zip 394 self._gpu = args.gpu 395 self._disk_type = (args.disk_type or self._cfg.disk_type) 396 self._base_instance_num = args.base_instance_num 397 self._gce_only = args.gce_only 398 self._gce_metadata = create_common.ParseKeyValuePairArgs(args.gce_metadata) 399 self._stable_host_image_name = ( 400 args.stable_host_image_name or self._cfg.stable_host_image_name) 401 402 self._stable_cheeps_host_image_name = args.stable_cheeps_host_image_name 403 self._stable_cheeps_host_image_project = args.stable_cheeps_host_image_project 404 self._username = args.username 405 self._password = args.password 406 self._cheeps_betty_image = ( 407 args.cheeps_betty_image or self._cfg.betty_image) 408 self._cheeps_features = args.cheeps_features 409 410 self._boot_timeout_secs = args.boot_timeout_secs 411 self._ins_timeout_secs = args.ins_timeout_secs 412 self._launch_args = " ".join( 413 list(filter(None, [self._cfg.launch_args, args.launch_args]))) 414 self._remote_fetch = args.remote_fetch 415 self._webrtc_device_id = args.webrtc_device_id 416 self._connect_hostname = args.connect_hostname or self._cfg.connect_hostname 417 self._fetch_cvd_wrapper = args.fetch_cvd_wrapper 418 self._fetch_cvd_version = self._GetFetchCVDVersion(args) 419 420 if args.reuse_gce: 421 if args.reuse_gce != constants.SELECT_ONE_GCE_INSTANCE: 422 if list_instance.GetInstancesFromInstanceNames( 423 self._cfg, [args.reuse_gce]): 424 self._instance_name_to_reuse = args.reuse_gce 425 if self._instance_name_to_reuse is None: 426 instance = list_instance.ChooseOneRemoteInstance(self._cfg) 427 self._instance_name_to_reuse = instance.name 428 429 def _GetFetchCVDVersion(self, args): 430 """Get the fetch_cvd version. 431 432 Acloud will get the LKGB of fetch_cvd if no version specified. 433 434 Args: 435 args: Namespace object from argparse.parse_args. 436 437 Returns: 438 The build id of fetch_cvd. 439 """ 440 if args.fetch_cvd_build_id: 441 return args.fetch_cvd_build_id 442 return constants.LKGB 443 444 @staticmethod 445 def _GetFlavorFromString(flavor_string): 446 """Get flavor name from flavor string. 447 448 Flavor string can come from the zipped image name or the lunch target. 449 e.g. 450 If flavor_string come from zipped name:aosp_cf_x86_phone-img-5455843.zip 451 , then "phone" is the flavor. 452 If flavor_string come from a lunch'd target:aosp_cf_x86_auto-userdebug, 453 then "auto" is the flavor. 454 455 Args: 456 flavor_string: String which contains flavor.It can be a 457 build target or filename. 458 459 Returns: 460 String of flavor name. None if flavor can't be determined. 461 """ 462 for flavor in constants.ALL_FLAVORS: 463 if re.match(r"(.*_)?%s" % flavor, flavor_string): 464 return flavor 465 466 logger.debug("Unable to determine flavor from build target: %s", 467 flavor_string) 468 return None 469 470 def _ProcessLocalImageArgs(self, args): 471 """Get local image path. 472 473 Args: 474 args: Namespace object from argparse.parse_args. 475 """ 476 if self._avd_type == constants.TYPE_CF: 477 self._ProcessCFLocalImageArgs(args.local_image, args.flavor) 478 elif self._avd_type == constants.TYPE_FVP: 479 self._ProcessFVPLocalImageArgs() 480 elif self._avd_type == constants.TYPE_GF: 481 local_image_path = self._GetLocalImagePath(args.local_image) 482 if os.path.isdir(local_image_path): 483 self._local_image_dir = local_image_path 484 else: 485 self._local_image_artifact = local_image_path 486 elif self._avd_type == constants.TYPE_GCE: 487 self._local_image_artifact = self._GetGceLocalImagePath( 488 args.local_image) 489 else: 490 raise errors.CreateError( 491 "Local image doesn't support the AVD type: %s" % self._avd_type 492 ) 493 494 @staticmethod 495 def _GetGceLocalImagePath(local_image_dir): 496 """Get gce local image path. 497 498 Choose image file in local_image_dir over $ANDROID_PRODUCT_OUT. 499 There are various img files so we prioritize returning the one we find 500 first based in the specified order in _GCE_LOCAL_IMAGE_CANDIDATES. 501 502 Args: 503 local_image_dir: A string to specify local image dir. 504 505 Returns: 506 String, image file path if exists. 507 508 Raises: 509 errors.ImgDoesNotExist if image doesn't exist. 510 """ 511 # IF the user specified a file, return it 512 if local_image_dir and os.path.isfile(local_image_dir): 513 return local_image_dir 514 515 # If the user didn't specify a dir, assume $ANDROID_PRODUCT_OUT 516 if not local_image_dir: 517 local_image_dir = utils.GetBuildEnvironmentVariable( 518 _ENV_ANDROID_PRODUCT_OUT) 519 520 for img_name in _GCE_LOCAL_IMAGE_CANDIDATES: 521 full_file_path = os.path.join(local_image_dir, img_name) 522 if os.path.exists(full_file_path): 523 return full_file_path 524 525 raise errors.ImgDoesNotExist("Could not find any GCE images (%s), you " 526 "can build them via \"m dist\"" % 527 ", ".join(_GCE_LOCAL_IMAGE_CANDIDATES)) 528 529 @staticmethod 530 def _GetLocalImagePath(local_image_arg): 531 """Get local image path from argument or environment variable. 532 533 Args: 534 local_image_arg: The path to the unzipped image package. If the 535 value is empty, this method returns 536 ANDROID_PRODUCT_OUT in build environment. 537 538 Returns: 539 String, the path to the image file or directory. 540 541 Raises: 542 errors.GetLocalImageError if the path does not exist. 543 """ 544 if local_image_arg == constants.FIND_IN_BUILD_ENV: 545 image_path = utils.GetBuildEnvironmentVariable( 546 constants.ENV_ANDROID_PRODUCT_OUT) 547 else: 548 image_path = local_image_arg 549 550 if not os.path.exists(image_path): 551 raise errors.GetLocalImageError("%s does not exist." % 552 local_image_arg) 553 return image_path 554 555 def _ProcessCFLocalImageArgs(self, local_image_arg, flavor_arg): 556 """Get local built image path for cuttlefish-type AVD. 557 558 Two scenarios of using --local-image: 559 - Without a following argument 560 Set flavor string if the required images are in $ANDROID_PRODUCT_OUT, 561 - With a following filename/dirname 562 Set flavor string from the specified image/dir name. 563 564 Args: 565 local_image_arg: String of local image args. 566 flavor_arg: String of flavor arg 567 568 """ 569 flavor_from_build_string = None 570 if local_image_arg == constants.FIND_IN_BUILD_ENV: 571 self._CheckCFBuildTarget(self._instance_type) 572 local_image_path = utils.GetBuildEnvironmentVariable( 573 _ENV_ANDROID_PRODUCT_OUT) 574 # Since dir is provided, check that any images exist to ensure user 575 # didn't forget to 'make' before launch AVD. 576 image_list = glob.glob(os.path.join(local_image_path, "*.img")) 577 if not image_list: 578 raise errors.GetLocalImageError( 579 "No image found(Did you choose a lunch target and run `m`?)" 580 ": %s.\n " % local_image_path) 581 else: 582 local_image_path = local_image_arg 583 584 if os.path.isfile(local_image_path): 585 self._local_image_artifact = local_image_arg 586 flavor_from_build_string = self._GetFlavorFromString( 587 self._local_image_artifact) 588 # Since file is provided and I assume it's a zip, so print the 589 # warning message. 590 utils.PrintColorString(_LOCAL_ZIP_WARNING_MSG, 591 utils.TextColors.WARNING) 592 else: 593 self._local_image_dir = local_image_path 594 try: 595 flavor_from_build_string = self._GetFlavorFromString( 596 utils.GetBuildEnvironmentVariable(constants.ENV_BUILD_TARGET)) 597 except errors.GetAndroidBuildEnvVarError: 598 logger.debug("Unable to determine flavor from env variable: %s", 599 constants.ENV_BUILD_TARGET) 600 601 if flavor_from_build_string and not flavor_arg: 602 self._flavor = flavor_from_build_string 603 604 def _ProcessFVPLocalImageArgs(self): 605 """Get local built image path for FVP-type AVD.""" 606 build_target = utils.GetBuildEnvironmentVariable( 607 constants.ENV_BUILD_TARGET) 608 if build_target != "fvp": 609 utils.PrintColorString( 610 "%s is not an fvp target (Try lunching fvp-eng " 611 "and running 'm')" % build_target, 612 utils.TextColors.WARNING) 613 self._local_image_dir = utils.GetBuildEnvironmentVariable( 614 _ENV_ANDROID_PRODUCT_OUT) 615 616 # Since dir is provided, so checking that any images exist to ensure 617 # user didn't forget to 'make' before launch AVD. 618 image_list = glob.glob(os.path.join(self.local_image_dir, "*.img")) 619 if not image_list: 620 raise errors.GetLocalImageError( 621 "No image found(Did you choose a lunch target and run `m`?)" 622 ": %s.\n " % self._local_image_dir) 623 624 def _ProcessRemoteBuildArgs(self, args): 625 """Get the remote build args. 626 627 Some of the acloud magic happens here, we will infer some of these 628 values if the user hasn't specified them. 629 630 Args: 631 args: Namespace object from argparse.parse_args. 632 """ 633 self._remote_image[constants.BUILD_BRANCH] = args.branch 634 if not self._remote_image[constants.BUILD_BRANCH]: 635 self._remote_image[constants.BUILD_BRANCH] = self._GetBuildBranch( 636 args.build_id, args.build_target) 637 638 self._remote_image[constants.BUILD_TARGET] = args.build_target 639 if not self._remote_image[constants.BUILD_TARGET]: 640 self._remote_image[constants.BUILD_TARGET] = self._GetBuildTarget( 641 args, self._remote_image[constants.BUILD_BRANCH]) 642 else: 643 # If flavor isn't specified, try to infer it from build target, 644 # if we can't, just default to phone flavor. 645 self._flavor = args.flavor or self._GetFlavorFromString( 646 self._remote_image[constants.BUILD_TARGET]) or constants.FLAVOR_PHONE 647 # infer avd_type from build_target. 648 for avd_type, avd_type_abbr in constants.AVD_TYPES_MAPPING.items(): 649 if re.match(r"(.*_)?%s_" % avd_type_abbr, 650 self._remote_image[constants.BUILD_TARGET]): 651 self._avd_type = avd_type 652 break 653 654 self._remote_image[constants.BUILD_ID] = args.build_id 655 if not self._remote_image[constants.BUILD_ID]: 656 build_client = android_build_client.AndroidBuildClient( 657 auth.CreateCredentials(self._cfg)) 658 659 self._remote_image[constants.BUILD_ID] = build_client.GetLKGB( 660 self._remote_image[constants.BUILD_TARGET], 661 self._remote_image[constants.BUILD_BRANCH]) 662 663 # Process system image, kernel image, bootloader, and otatools. 664 self._system_build_info = {constants.BUILD_ID: args.system_build_id, 665 constants.BUILD_BRANCH: args.system_branch, 666 constants.BUILD_TARGET: args.system_build_target} 667 self._ota_build_info = {constants.BUILD_ID: args.ota_build_id, 668 constants.BUILD_BRANCH: args.ota_branch, 669 constants.BUILD_TARGET: args.ota_build_target} 670 self._kernel_build_info = {constants.BUILD_ID: args.kernel_build_id, 671 constants.BUILD_BRANCH: args.kernel_branch, 672 constants.BUILD_TARGET: args.kernel_build_target} 673 self._boot_build_info = {constants.BUILD_ID: args.boot_build_id, 674 constants.BUILD_BRANCH: args.boot_branch, 675 constants.BUILD_TARGET: args.boot_build_target, 676 constants.BUILD_ARTIFACT: args.boot_artifact} 677 self._bootloader_build_info = { 678 constants.BUILD_ID: args.bootloader_build_id, 679 constants.BUILD_BRANCH: args.bootloader_branch, 680 constants.BUILD_TARGET: args.bootloader_build_target} 681 self._android_efi_loader_build_info = { 682 constants.BUILD_ID: args.android_efi_loader_build_id, 683 constants.BUILD_ARTIFACT: args.android_efi_loader_artifact} 684 self._host_package_build_info = { 685 constants.BUILD_ID: args.host_package_build_id, 686 constants.BUILD_BRANCH: args.host_package_branch, 687 constants.BUILD_TARGET: args.host_package_build_target} 688 689 @staticmethod 690 def _CheckCFBuildTarget(instance_type): 691 """Check build target for the given instance type 692 693 Args: 694 instance_type: String of instance type 695 696 Raises: 697 errors.GetLocalImageError if the pattern is not match with 698 current build target. 699 """ 700 build_target = utils.GetBuildEnvironmentVariable( 701 constants.ENV_BUILD_TARGET) 702 pattern = constants.CF_AVD_BUILD_TARGET_PATTERN_MAPPING[instance_type] 703 if pattern not in build_target: 704 utils.PrintColorString( 705 "%s is not a %s target (Try lunching a proper cuttlefish " 706 "target and running 'm')" % (build_target, pattern), 707 utils.TextColors.WARNING) 708 709 @staticmethod 710 def _GetGitRemote(): 711 """Get the remote repo. 712 713 We'll go to a project we know exists (tools/acloud) and grab the git 714 remote output from there. 715 716 Returns: 717 remote: String, git remote (e.g. "aosp"). 718 """ 719 try: 720 android_build_top = os.environ[constants.ENV_ANDROID_BUILD_TOP] 721 except KeyError as e: 722 raise errors.GetAndroidBuildEnvVarError( 723 "Could not get environment var: %s\n" 724 "Try to run '#source build/envsetup.sh && lunch <target>'" 725 % _ENV_ANDROID_BUILD_TOP) from e 726 727 acloud_project = os.path.join(android_build_top, "tools", "acloud") 728 return EscapeAnsi(utils.CheckOutput(_COMMAND_GIT_REMOTE, 729 cwd=acloud_project).strip()) 730 731 def _GetBuildBranch(self, build_id, build_target): 732 """Infer build branch if user didn't specify branch name. 733 734 Args: 735 build_id: String, Build id, e.g. "2263051", "P2804227" 736 build_target: String, the build target, e.g. cf_x86_phone-userdebug 737 738 Returns: 739 String, name of build branch. 740 """ 741 # Infer branch from build_target and build_id 742 if build_id and build_target: 743 build_client = android_build_client.AndroidBuildClient( 744 auth.CreateCredentials(self._cfg)) 745 return build_client.GetBranch(build_target, build_id) 746 747 return self._GetBranchFromRepo() 748 749 def _GetBranchFromRepo(self): 750 """Get branch information from command "repo info". 751 752 If branch can't get from "repo info", it will be set as default branch 753 "aosp-master". 754 755 Returns: 756 branch: String, git branch name. e.g. "aosp-master" 757 """ 758 branch = None 759 # TODO(149460014): Migrate acloud to py3, then remove this 760 # workaround. 761 env = os.environ.copy() 762 env.pop("PYTHONPATH", None) 763 logger.info("Running command \"%s\"", _COMMAND_REPO_INFO) 764 # TODO(154173071): Migrate acloud to py3, then apply Popen to append with encoding 765 process = subprocess.Popen(_COMMAND_REPO_INFO, shell=True, stdin=None, 766 stdout=subprocess.PIPE, 767 stderr=subprocess.STDOUT, env=env, 768 universal_newlines=True) 769 timer = threading.Timer(_REPO_TIMEOUT, process.kill) 770 timer.start() 771 stdout, _ = process.communicate() 772 if stdout: 773 for line in stdout.splitlines(): 774 match = _BRANCH_RE.match(EscapeAnsi(line)) 775 if match: 776 branch_prefix = _BRANCH_PREFIX.get(self._GetGitRemote(), 777 _DEFAULT_BRANCH_PREFIX) 778 branch = branch_prefix + match.group("branch") 779 timer.cancel() 780 if branch: 781 return branch 782 utils.PrintColorString( 783 "Unable to determine your repo branch, defaulting to %s" 784 % _DEFAULT_BRANCH, utils.TextColors.WARNING) 785 return _DEFAULT_BRANCH 786 787 def _GetBuildTarget(self, args, branch): 788 """Infer build target if user doesn't specified target name. 789 790 Target = {REPO_PREFIX}{avd_type}_{bitness}_{flavor}- 791 {DEFAULT_BUILD_TARGET_TYPE}. 792 Example target: aosp_cf_x86_64_phone-userdebug 793 794 Args: 795 args: Namespace object from argparse.parse_args. 796 branch: String, name of build branch. 797 798 Returns: 799 build_target: String, name of build target. 800 """ 801 branch_prefix = re.split("-|_", branch)[0] 802 return "%s%s_%s_%s%s-%s" % ( 803 _BRANCH_TARGET_PREFIX.get(branch_prefix, ""), 804 constants.AVD_TYPES_MAPPING[args.avd_type], 805 _DEFAULT_BUILD_BITNESS, self._flavor, 806 _BRANCH_TARGET_TRUNK_STAGEING.get(branch, ""), 807 _DEFAULT_BUILD_TYPE) 808 809 @property 810 def instance_type(self): 811 """Return the instance type.""" 812 return self._instance_type 813 814 @property 815 def image_source(self): 816 """Return the image type.""" 817 return self._image_source 818 819 @property 820 def hw_property(self): 821 """Return the hw_property.""" 822 return self._hw_property 823 824 @property 825 def hw_customize(self): 826 """Return the hw_customize.""" 827 return self._hw_customize 828 829 @property 830 def local_image_dir(self): 831 """Return local image dir.""" 832 return self._local_image_dir 833 834 @property 835 def local_image_artifact(self): 836 """Return local image artifact.""" 837 return self._local_image_artifact 838 839 @property 840 def local_instance_dir(self): 841 """Return local instance directory.""" 842 return self._local_instance_dir 843 844 @property 845 def local_kernel_image(self): 846 """Return local kernel image path.""" 847 return self._local_kernel_image 848 849 @property 850 def local_system_image(self): 851 """Return local system image path.""" 852 return self._local_system_image 853 854 @property 855 def local_system_dlkm_image(self): 856 """Return local system_dlkm image path.""" 857 return self._local_system_dlkm_image 858 859 @property 860 def local_vendor_image(self): 861 """Return local vendor image path.""" 862 return self._local_vendor_image 863 864 @property 865 def local_vendor_boot_image(self): 866 """Return local vendor boot image path.""" 867 return self._local_vendor_boot_image 868 869 @property 870 def local_tool_dirs(self): 871 """Return a list of local tool directories.""" 872 return self._local_tool_dirs 873 874 @property 875 def avd_type(self): 876 """Return the avd type.""" 877 return self._avd_type 878 879 @property 880 def autoconnect(self): 881 """autoconnect. 882 883 args.autoconnect could pass as Boolean or String. 884 885 Return: Boolean, True only if self._autoconnect is not False. 886 """ 887 return self._autoconnect is not False 888 889 @property 890 def connect_adb(self): 891 """Auto-connect to adb. 892 893 Return: Boolean, whether autoconnect is enabled. 894 """ 895 return self._autoconnect is not False 896 897 @property 898 def connect_vnc(self): 899 """Launch vnc. 900 901 Return: Boolean, True if self._autoconnect is 'vnc'. 902 """ 903 return self._autoconnect == constants.INS_KEY_VNC 904 905 @property 906 def connect_webrtc(self): 907 """Auto-launch webRTC AVD on the browser. 908 909 Return: Boolean, True if args.autoconnect is "webrtc". 910 """ 911 return self._autoconnect == constants.INS_KEY_WEBRTC 912 913 @property 914 def unlock_screen(self): 915 """Return unlock_screen.""" 916 return self._unlock_screen 917 918 @property 919 def remote_image(self): 920 """Return the remote image.""" 921 return self._remote_image 922 923 @property 924 def remote_fetch(self): 925 """Fetch cvd in remote host. 926 927 Return: Boolean, whether fetch cvd in remote host. 928 """ 929 return self._remote_fetch is True 930 931 @property 932 def fetch_cvd_wrapper(self): 933 """use fetch_cvd wrapper 934 935 Return: Boolean, whether fetch cvd in remote host. 936 """ 937 return self._fetch_cvd_wrapper 938 939 @property 940 def fetch_cvd_version(self): 941 """Return fetch_cvd_version.""" 942 return self._fetch_cvd_version 943 944 @property 945 def num(self): 946 """Return num of instances.""" 947 return self._num_of_instances 948 949 @property 950 def num_avds_per_instance(self): 951 """Return num_avds_per_instance.""" 952 return self._num_avds_per_instance 953 954 @property 955 def report_internal_ip(self): 956 """Return report internal ip.""" 957 return self._report_internal_ip 958 959 @property 960 def disable_external_ip(self): 961 """Return disable_external_ip.""" 962 return self._disable_external_ip 963 964 @property 965 def kernel_build_info(self): 966 """Return kernel build info.""" 967 return self._kernel_build_info 968 969 @property 970 def boot_build_info(self): 971 """Return boot build info.""" 972 return self._boot_build_info 973 974 @property 975 def bootloader_build_info(self): 976 """Return bootloader build info.""" 977 return self._bootloader_build_info 978 979 @property 980 def android_efi_loader_build_info(self): 981 """Return android efi loader build info.""" 982 return self._android_efi_loader_build_info 983 984 @property 985 def flavor(self): 986 """Return flavor.""" 987 return self._flavor 988 989 @property 990 def cfg(self): 991 """Return cfg instance.""" 992 return self._cfg 993 994 @property 995 def image_download_dir(self): 996 """Return image download dir.""" 997 return self._image_download_dir 998 999 @image_download_dir.setter 1000 def image_download_dir(self, value): 1001 """Set image download dir.""" 1002 self._image_download_dir = value 1003 1004 @property 1005 def serial_log_file(self): 1006 """Return serial log file path.""" 1007 return self._serial_log_file 1008 1009 @property 1010 def disk_type(self): 1011 """Return disk type.""" 1012 return self._disk_type 1013 1014 @property 1015 def base_instance_num(self): 1016 """Return base instance num.""" 1017 return self._base_instance_num 1018 1019 @property 1020 def gpu(self): 1021 """Return gpu.""" 1022 return self._gpu 1023 1024 @property 1025 def emulator_build_id(self): 1026 """Return emulator_build_id.""" 1027 return self._emulator_build_id 1028 1029 @property 1030 def emulator_build_target(self): 1031 """Return emulator_build_target.""" 1032 return self._emulator_build_target 1033 1034 @property 1035 def emulator_zip(self): 1036 """Return emulator_zip.""" 1037 return self._emulator_zip 1038 1039 @property 1040 def client_adb_port(self): 1041 """Return the client adb port.""" 1042 return self._client_adb_port 1043 1044 @property 1045 def stable_host_image_name(self): 1046 """Return the Cuttlefish host image name.""" 1047 return self._stable_host_image_name 1048 1049 @property 1050 def stable_cheeps_host_image_name(self): 1051 """Return the Cheeps host image name.""" 1052 return self._stable_cheeps_host_image_name 1053 1054 # pylint: disable=invalid-name 1055 @property 1056 def stable_cheeps_host_image_project(self): 1057 """Return the project hosting the Cheeps host image.""" 1058 return self._stable_cheeps_host_image_project 1059 1060 @property 1061 def username(self): 1062 """Return username.""" 1063 return self._username 1064 1065 @property 1066 def password(self): 1067 """Return password.""" 1068 return self._password 1069 1070 @property 1071 def cheeps_betty_image(self): 1072 """Return cheeps_betty_image.""" 1073 return self._cheeps_betty_image 1074 1075 @property 1076 def cheeps_features(self): 1077 """Return cheeps_features.""" 1078 return self._cheeps_features 1079 1080 @property 1081 def boot_timeout_secs(self): 1082 """Return boot_timeout_secs.""" 1083 return self._boot_timeout_secs 1084 1085 @property 1086 def ins_timeout_secs(self): 1087 """Return ins_timeout_secs.""" 1088 return self._ins_timeout_secs 1089 1090 @property 1091 def ota_build_info(self): 1092 """Return ota_build_info.""" 1093 return self._ota_build_info 1094 1095 @property 1096 def host_package_build_info(self): 1097 """Return host_package_build_info.""" 1098 return self._host_package_build_info 1099 1100 @property 1101 def system_build_info(self): 1102 """Return system_build_info.""" 1103 return self._system_build_info 1104 1105 @property 1106 def local_instance_id(self): 1107 """Return local_instance_id.""" 1108 return self._local_instance_id 1109 1110 @property 1111 def instance_name_to_reuse(self): 1112 """Return instance_name_to_reuse.""" 1113 return self._instance_name_to_reuse 1114 1115 @property 1116 def remote_host(self): 1117 """Return host.""" 1118 return self._remote_host 1119 1120 @property 1121 def remote_image_dir(self): 1122 """Return remote_image_dir.""" 1123 return self._remote_image_dir 1124 1125 @property 1126 def host_user(self): 1127 """Return host_user.""" 1128 return self._host_user 1129 1130 @property 1131 def host_ssh_private_key_path(self): 1132 """Return host_ssh_private_key_path.""" 1133 return self._host_ssh_private_key_path 1134 1135 @property 1136 def no_pull_log(self): 1137 """Return no_pull_log.""" 1138 return self._no_pull_log 1139 1140 @property 1141 def mkcert(self): 1142 """Return mkcert.""" 1143 return self._mkcert 1144 1145 @property 1146 def gce_metadata(self): 1147 """Return gce_metadata.""" 1148 return self._gce_metadata 1149 1150 @property 1151 def gce_only(self): 1152 """Return gce_only.""" 1153 return self._gce_only 1154 1155 @property 1156 def oxygen(self): 1157 """Return oxygen.""" 1158 return self._oxygen 1159 1160 @property 1161 def openwrt(self): 1162 """Return openwrt.""" 1163 return self._openwrt 1164 1165 @property 1166 def use_launch_cvd(self): 1167 """Return use_launch_cvd.""" 1168 return self._use_launch_cvd 1169 1170 @property 1171 def launch_args(self): 1172 """Return launch_args.""" 1173 return self._launch_args 1174 1175 @property 1176 def cvd_host_package(self): 1177 """Return cvd_host_package.""" 1178 return self._cvd_host_package 1179 1180 @property 1181 def extra_files(self): 1182 """Return extra_files.""" 1183 return self._extra_files 1184 1185 @property 1186 def force_sync(self): 1187 """Return force_sync.""" 1188 return self._force_sync 1189 1190 @property 1191 def webrtc_device_id(self): 1192 """Return webrtc_device_id.""" 1193 return self._webrtc_device_id 1194 1195 @property 1196 def connect_hostname(self): 1197 """Return connect_hostname""" 1198 return self._connect_hostname 1199