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