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