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