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" 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 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._autoconnect = None 106 self._instance_name_to_reuse = None 107 self._unlock_screen = None 108 self._report_internal_ip = None 109 self._avd_type = None 110 self._flavor = None 111 self._image_source = None 112 self._instance_type = None 113 self._local_image_dir = None 114 self._local_image_artifact = None 115 self._local_system_image_dir = None 116 self._local_tool_dirs = None 117 self._image_download_dir = None 118 self._num_of_instances = None 119 self._no_pull_log = None 120 self._remote_image = None 121 self._system_build_info = None 122 self._kernel_build_info = None 123 self._hw_property = None 124 self._remote_host = None 125 self._host_user = None 126 self._host_ssh_private_key_path = None 127 # Create config instance for android_build_client to query build api. 128 self._cfg = config.GetAcloudConfig(args) 129 # Reporting args. 130 self._serial_log_file = None 131 # gpu and emulator_build_id is only used for goldfish avd_type. 132 self._gpu = None 133 self._emulator_build_id = None 134 135 # Fields only used for cheeps type. 136 self._stable_cheeps_host_image_name = None 137 self._stable_cheeps_host_image_project = None 138 self._username = None 139 self._password = None 140 141 # The maximum time in seconds used to wait for the AVD to boot. 142 self._boot_timeout_secs = None 143 # The maximum time in seconds used to wait for the instance ready. 144 self._ins_timeout_secs = None 145 146 # The local instance id 147 self._local_instance_id = None 148 149 self._ProcessArgs(args) 150 151 def __repr__(self): 152 """Let's make it easy to see what this class is holding.""" 153 # TODO: I'm pretty sure there's a better way to do this, but I'm not 154 # quite sure what that would be. 155 representation = [] 156 representation.append("") 157 representation.append(" - instance_type: %s" % self._instance_type) 158 representation.append(" - avd type: %s" % self._avd_type) 159 representation.append(" - flavor: %s" % self._flavor) 160 representation.append(" - autoconnect: %s" % self._autoconnect) 161 representation.append(" - num of instances requested: %s" % 162 self._num_of_instances) 163 representation.append(" - image source type: %s" % 164 self._image_source) 165 image_summary = None 166 image_details = None 167 if self._image_source == constants.IMAGE_SRC_LOCAL: 168 image_summary = "local image dir" 169 image_details = self._local_image_dir 170 representation.append(" - instance id: %s" % self._local_instance_id) 171 elif self._image_source == constants.IMAGE_SRC_REMOTE: 172 image_summary = "remote image details" 173 image_details = self._remote_image 174 representation.append(" - %s: %s" % (image_summary, image_details)) 175 representation.append(" - hw properties: %s" % 176 self._hw_property) 177 return "\n".join(representation) 178 179 def _ProcessArgs(self, args): 180 """Main entry point to process args for the different type of args. 181 182 Split up the arg processing into related areas (image, instance type, 183 etc) so that we don't have one huge monolilthic method that does 184 everything. It makes it easier to review, write tests, and maintain. 185 186 Args: 187 args: Namespace object from argparse.parse_args. 188 """ 189 self._ProcessMiscArgs(args) 190 self._ProcessImageArgs(args) 191 self._ProcessHWPropertyArgs(args) 192 193 def _ProcessImageArgs(self, args): 194 """ Process Image Args. 195 196 Args: 197 args: Namespace object from argparse.parse_args. 198 """ 199 # If user didn't specify --local-image, infer remote image args 200 if args.local_image == "": 201 self._image_source = constants.IMAGE_SRC_REMOTE 202 if (self._avd_type == constants.TYPE_GF and 203 self._instance_type != constants.INSTANCE_TYPE_REMOTE): 204 raise errors.UnsupportedInstanceImageType( 205 "unsupported creation of avd type: %s, " 206 "instance type: %s, image source: %s" % 207 (self._avd_type, self._instance_type, self._image_source)) 208 self._ProcessRemoteBuildArgs(args) 209 else: 210 self._image_source = constants.IMAGE_SRC_LOCAL 211 self._ProcessLocalImageArgs(args) 212 213 self.image_download_dir = ( 214 args.image_download_dir if args.image_download_dir 215 else tempfile.gettempdir()) 216 217 @staticmethod 218 def _ParseHWPropertyStr(hw_property_str): 219 """Parse string to dict. 220 221 Args: 222 hw_property_str: A hw properties string. 223 224 Returns: 225 Dict converted from a string. 226 227 Raises: 228 error.MalformedHWPropertyError: If hw_property_str is malformed. 229 """ 230 hw_dict = create_common.ParseHWPropertyArgs(hw_property_str) 231 arg_hw_properties = {} 232 for key, value in hw_dict.items(): 233 # Parsing HW properties int to avdspec. 234 if key == constants.HW_ALIAS_RESOLUTION: 235 match = _RE_RES.match(value) 236 if match: 237 arg_hw_properties[_X_RES] = match.group("x_res") 238 arg_hw_properties[_Y_RES] = match.group("y_res") 239 else: 240 raise errors.InvalidHWPropertyError( 241 "[%s] is an invalid resolution. Example:1280x800" % value) 242 elif key in [constants.HW_ALIAS_MEMORY, constants.HW_ALIAS_DISK]: 243 match = _RE_MEMORY.match(value) 244 if match and match.group("gb_size"): 245 arg_hw_properties[key] = str( 246 int(match.group("gb_size")) * 1024) 247 elif match and match.group("mb_size"): 248 arg_hw_properties[key] = match.group("mb_size") 249 else: 250 raise errors.InvalidHWPropertyError( 251 "Expected gb size.[%s] is not allowed. Example:4g" % value) 252 elif key in [constants.HW_ALIAS_CPUS, constants.HW_ALIAS_DPI]: 253 if not _RE_INT.match(value): 254 raise errors.InvalidHWPropertyError( 255 "%s value [%s] is not an integer." % (key, value)) 256 arg_hw_properties[key] = value 257 258 return arg_hw_properties 259 260 def _ProcessHWPropertyArgs(self, args): 261 """Get the HW properties from argparse.parse_args. 262 263 This method will initialize _hw_property in the following 264 manner: 265 1. Get default hw properties from config. 266 2. Override by hw_property args. 267 268 Args: 269 args: Namespace object from argparse.parse_args. 270 """ 271 self._cfg.OverrideHwPropertyWithFlavor(self._flavor) 272 self._hw_property = {} 273 self._hw_property = self._ParseHWPropertyStr(self._cfg.hw_property) 274 logger.debug("Default hw property for [%s] flavor: %s", self._flavor, 275 self._hw_property) 276 277 if args.hw_property: 278 arg_hw_property = self._ParseHWPropertyStr(args.hw_property) 279 logger.debug("Use custom hw property: %s", arg_hw_property) 280 self._hw_property.update(arg_hw_property) 281 282 def _ProcessMiscArgs(self, args): 283 """These args we can take as and don't belong to a group of args. 284 285 Args: 286 args: Namespace object from argparse.parse_args. 287 """ 288 self._autoconnect = args.autoconnect 289 self._unlock_screen = args.unlock_screen 290 self._report_internal_ip = args.report_internal_ip 291 self._avd_type = args.avd_type 292 self._flavor = args.flavor or constants.FLAVOR_PHONE 293 if args.remote_host: 294 self._instance_type = constants.INSTANCE_TYPE_HOST 295 else: 296 self._instance_type = (constants.INSTANCE_TYPE_LOCAL 297 if args.local_instance else 298 constants.INSTANCE_TYPE_REMOTE) 299 self._remote_host = args.remote_host 300 self._host_user = args.host_user 301 self._host_ssh_private_key_path = args.host_ssh_private_key_path 302 self._local_instance_id = args.local_instance 303 self._local_tool_dirs = args.local_tool 304 self._num_of_instances = args.num 305 self._no_pull_log = args.no_pull_log 306 self._serial_log_file = args.serial_log_file 307 self._emulator_build_id = args.emulator_build_id 308 self._gpu = args.gpu 309 310 self._stable_cheeps_host_image_name = args.stable_cheeps_host_image_name 311 self._stable_cheeps_host_image_project = args.stable_cheeps_host_image_project 312 self._username = args.username 313 self._password = args.password 314 315 self._boot_timeout_secs = args.boot_timeout_secs 316 self._ins_timeout_secs = args.ins_timeout_secs 317 318 if args.reuse_gce: 319 if args.reuse_gce != constants.SELECT_ONE_GCE_INSTANCE: 320 if list_instance.GetInstancesFromInstanceNames( 321 self._cfg, [args.reuse_gce]): 322 self._instance_name_to_reuse = args.reuse_gce 323 if self._instance_name_to_reuse is None: 324 instance = list_instance.ChooseOneRemoteInstance(self._cfg) 325 self._instance_name_to_reuse = instance.name 326 327 @staticmethod 328 def _GetFlavorFromString(flavor_string): 329 """Get flavor name from flavor string. 330 331 Flavor string can come from the zipped image name or the lunch target. 332 e.g. 333 If flavor_string come from zipped name:aosp_cf_x86_phone-img-5455843.zip 334 , then "phone" is the flavor. 335 If flavor_string come from a lunch'd target:aosp_cf_x86_auto-userdebug, 336 then "auto" is the flavor. 337 338 Args: 339 flavor_string: String which contains flavor.It can be a 340 build target or filename. 341 342 Returns: 343 String of flavor name. None if flavor can't be determined. 344 """ 345 for flavor in constants.ALL_FLAVORS: 346 if re.match(r"(.*_)?%s" % flavor, flavor_string): 347 return flavor 348 349 logger.debug("Unable to determine flavor from build target: %s", 350 flavor_string) 351 return None 352 353 def _ProcessLocalImageArgs(self, args): 354 """Get local image path. 355 356 Args: 357 args: Namespace object from argparse.parse_args. 358 """ 359 if self._avd_type == constants.TYPE_CF: 360 self._ProcessCFLocalImageArgs(args.local_image, args.flavor) 361 elif self._avd_type == constants.TYPE_GF: 362 self._local_image_dir = self._ProcessGFLocalImageArgs( 363 args.local_image) 364 if args.local_system_image != "": 365 self._local_system_image_dir = self._ProcessGFLocalImageArgs( 366 args.local_system_image) 367 elif self._avd_type == constants.TYPE_GCE: 368 self._local_image_artifact = self._GetGceLocalImagePath( 369 args.local_image) 370 else: 371 raise errors.CreateError( 372 "Local image doesn't support the AVD type: %s" % self._avd_type 373 ) 374 375 @staticmethod 376 def _GetGceLocalImagePath(local_image_dir): 377 """Get gce local image path. 378 379 Choose image file in local_image_dir over $ANDROID_PRODUCT_OUT. 380 There are various img files so we prioritize returning the one we find 381 first based in the specified order in _GCE_LOCAL_IMAGE_CANDIDATES. 382 383 Args: 384 local_image_dir: A string to specify local image dir. 385 386 Returns: 387 String, image file path if exists. 388 389 Raises: 390 errors.ImgDoesNotExist if image doesn't exist. 391 """ 392 # IF the user specified a file, return it 393 if local_image_dir and os.path.isfile(local_image_dir): 394 return local_image_dir 395 396 # If the user didn't specify a dir, assume $ANDROID_PRODUCT_OUT 397 if not local_image_dir: 398 local_image_dir = utils.GetBuildEnvironmentVariable( 399 _ENV_ANDROID_PRODUCT_OUT) 400 401 for img_name in _GCE_LOCAL_IMAGE_CANDIDATES: 402 full_file_path = os.path.join(local_image_dir, img_name) 403 if os.path.exists(full_file_path): 404 return full_file_path 405 406 raise errors.ImgDoesNotExist("Could not find any GCE images (%s), you " 407 "can build them via \"m dist\"" % 408 ", ".join(_GCE_LOCAL_IMAGE_CANDIDATES)) 409 410 @staticmethod 411 def _ProcessGFLocalImageArgs(local_image_arg): 412 """Get local built image path for goldfish. 413 414 Args: 415 local_image_arg: The path to the unzipped update package or SDK 416 repository, i.e., <target>-img-<build>.zip or 417 sdk-repo-<os>-system-images-<build>.zip. 418 If the value is empty, this method returns 419 ANDROID_PRODUCT_OUT in build environment. 420 421 Returns: 422 String, the path to the image directory. 423 424 Raises: 425 errors.GetLocalImageError if the directory is not found. 426 """ 427 image_dir = (local_image_arg if local_image_arg else 428 utils.GetBuildEnvironmentVariable( 429 constants.ENV_ANDROID_PRODUCT_OUT)) 430 431 if not os.path.isdir(image_dir): 432 raise errors.GetLocalImageError( 433 "%s is not a directory." % image_dir) 434 435 return image_dir 436 437 def _ProcessCFLocalImageArgs(self, local_image_arg, flavor_arg): 438 """Get local built image path for cuttlefish-type AVD. 439 440 Two scenarios of using --local-image: 441 - Without a following argument 442 Set flavor string if the required images are in $ANDROID_PRODUCT_OUT, 443 - With a following filename/dirname 444 Set flavor string from the specified image/dir name. 445 446 Args: 447 local_image_arg: String of local image args. 448 flavor_arg: String of flavor arg 449 450 """ 451 flavor_from_build_string = None 452 local_image_path = local_image_arg or utils.GetBuildEnvironmentVariable( 453 _ENV_ANDROID_PRODUCT_OUT) 454 455 if os.path.isfile(local_image_path): 456 self._local_image_artifact = local_image_arg 457 flavor_from_build_string = self._GetFlavorFromString( 458 self._local_image_artifact) 459 # Since file is provided and I assume it's a zip, so print the 460 # warning message. 461 utils.PrintColorString(_LOCAL_ZIP_WARNING_MSG, 462 utils.TextColors.WARNING) 463 else: 464 self._local_image_dir = local_image_path 465 # Since dir is provided, so checking that any images exist to ensure 466 # user didn't forget to 'make' before launch AVD. 467 image_list = glob.glob(os.path.join(self.local_image_dir, "*.img")) 468 if not image_list: 469 raise errors.GetLocalImageError( 470 "No image found(Did you choose a lunch target and run `m`?)" 471 ": %s.\n " % self.local_image_dir) 472 473 try: 474 flavor_from_build_string = self._GetFlavorFromString( 475 utils.GetBuildEnvironmentVariable(constants.ENV_BUILD_TARGET)) 476 except errors.GetAndroidBuildEnvVarError: 477 logger.debug("Unable to determine flavor from env variable: %s", 478 constants.ENV_BUILD_TARGET) 479 480 if flavor_from_build_string and not flavor_arg: 481 self._flavor = flavor_from_build_string 482 483 def _ProcessRemoteBuildArgs(self, args): 484 """Get the remote build args. 485 486 Some of the acloud magic happens here, we will infer some of these 487 values if the user hasn't specified them. 488 489 Args: 490 args: Namespace object from argparse.parse_args. 491 """ 492 self._remote_image = {} 493 self._remote_image[constants.BUILD_BRANCH] = args.branch 494 if not self._remote_image[constants.BUILD_BRANCH]: 495 self._remote_image[constants.BUILD_BRANCH] = self._GetBuildBranch( 496 args.build_id, args.build_target) 497 498 self._remote_image[constants.BUILD_TARGET] = args.build_target 499 if not self._remote_image[constants.BUILD_TARGET]: 500 self._remote_image[constants.BUILD_TARGET] = self._GetBuildTarget(args) 501 else: 502 # If flavor isn't specified, try to infer it from build target, 503 # if we can't, just default to phone flavor. 504 self._flavor = args.flavor or self._GetFlavorFromString( 505 self._remote_image[constants.BUILD_TARGET]) or constants.FLAVOR_PHONE 506 # infer avd_type from build_target. 507 for avd_type, avd_type_abbr in constants.AVD_TYPES_MAPPING.items(): 508 if re.match(r"(.*_)?%s_" % avd_type_abbr, 509 self._remote_image[constants.BUILD_TARGET]): 510 self._avd_type = avd_type 511 break 512 513 self._remote_image[constants.BUILD_ID] = args.build_id 514 if not self._remote_image[constants.BUILD_ID]: 515 build_client = android_build_client.AndroidBuildClient( 516 auth.CreateCredentials(self._cfg)) 517 518 self._remote_image[constants.BUILD_ID] = build_client.GetLKGB( 519 self._remote_image[constants.BUILD_TARGET], 520 self._remote_image[constants.BUILD_BRANCH]) 521 522 # Process system image and kernel image. 523 self._system_build_info = {constants.BUILD_ID: args.system_build_id, 524 constants.BUILD_BRANCH: args.system_branch, 525 constants.BUILD_TARGET: args.system_build_target} 526 self._kernel_build_info = {constants.BUILD_ID: args.kernel_build_id, 527 constants.BUILD_BRANCH: args.kernel_branch, 528 constants.BUILD_TARGET: args.kernel_build_target} 529 530 @staticmethod 531 def _GetGitRemote(): 532 """Get the remote repo. 533 534 We'll go to a project we know exists (tools/acloud) and grab the git 535 remote output from there. 536 537 Returns: 538 remote: String, git remote (e.g. "aosp"). 539 """ 540 try: 541 android_build_top = os.environ[constants.ENV_ANDROID_BUILD_TOP] 542 except KeyError: 543 raise errors.GetAndroidBuildEnvVarError( 544 "Could not get environment var: %s\n" 545 "Try to run '#source build/envsetup.sh && lunch <target>'" 546 % _ENV_ANDROID_BUILD_TOP 547 ) 548 549 acloud_project = os.path.join(android_build_top, "tools", "acloud") 550 return EscapeAnsi(subprocess.check_output(_COMMAND_GIT_REMOTE, 551 cwd=acloud_project).strip()) 552 553 def _GetBuildBranch(self, build_id, build_target): 554 """Infer build branch if user didn't specify branch name. 555 556 Args: 557 build_id: String, Build id, e.g. "2263051", "P2804227" 558 build_target: String, the build target, e.g. cf_x86_phone-userdebug 559 560 Returns: 561 String, name of build branch. 562 """ 563 # Infer branch from build_target and build_id 564 if build_id and build_target: 565 build_client = android_build_client.AndroidBuildClient( 566 auth.CreateCredentials(self._cfg)) 567 return build_client.GetBranch(build_target, build_id) 568 569 return self._GetBranchFromRepo() 570 571 def _GetBranchFromRepo(self): 572 """Get branch information from command "repo info". 573 574 If branch can't get from "repo info", it will be set as default branch 575 "aosp-master". 576 577 Returns: 578 branch: String, git branch name. e.g. "aosp-master" 579 """ 580 branch = None 581 # TODO(149460014): Migrate acloud to py3, then remove this 582 # workaround. 583 env = os.environ.copy() 584 env.pop("PYTHONPATH", None) 585 logger.info("Running command \"%s\"", _COMMAND_REPO_INFO) 586 process = subprocess.Popen(_COMMAND_REPO_INFO, shell=True, stdin=None, 587 stdout=subprocess.PIPE, 588 stderr=subprocess.STDOUT, env=env) 589 timer = threading.Timer(_REPO_TIMEOUT, process.kill) 590 timer.start() 591 stdout, _ = process.communicate() 592 if stdout: 593 for line in stdout.splitlines(): 594 match = _BRANCH_RE.match(EscapeAnsi(line)) 595 if match: 596 branch_prefix = _BRANCH_PREFIX.get(self._GetGitRemote(), 597 _DEFAULT_BRANCH_PREFIX) 598 branch = branch_prefix + match.group("branch") 599 timer.cancel() 600 if branch: 601 return branch 602 utils.PrintColorString( 603 "Unable to determine your repo branch, defaulting to %s" 604 % _DEFAULT_BRANCH, utils.TextColors.WARNING) 605 return _DEFAULT_BRANCH 606 607 def _GetBuildTarget(self, args): 608 """Infer build target if user doesn't specified target name. 609 610 Target = {REPO_PREFIX}{avd_type}_{bitness}_{flavor}- 611 {DEFAULT_BUILD_TARGET_TYPE}. 612 Example target: aosp_cf_x86_phone-userdebug 613 614 Args: 615 args: Namespace object from argparse.parse_args. 616 617 Returns: 618 build_target: String, name of build target. 619 """ 620 branch = re.split("-|_", self._remote_image[constants.BUILD_BRANCH])[0] 621 return "%s%s_%s_%s-%s" % ( 622 _BRANCH_TARGET_PREFIX.get(branch, ""), 623 constants.AVD_TYPES_MAPPING[args.avd_type], 624 _DEFAULT_BUILD_BITNESS, self._flavor, 625 _DEFAULT_BUILD_TYPE) 626 627 @property 628 def instance_type(self): 629 """Return the instance type.""" 630 return self._instance_type 631 632 @property 633 def image_source(self): 634 """Return the image type.""" 635 return self._image_source 636 637 @property 638 def hw_property(self): 639 """Return the hw_property.""" 640 return self._hw_property 641 642 @property 643 def local_image_dir(self): 644 """Return local image dir.""" 645 return self._local_image_dir 646 647 @property 648 def local_image_artifact(self): 649 """Return local image artifact.""" 650 return self._local_image_artifact 651 652 @property 653 def local_system_image_dir(self): 654 """Return local system image dir.""" 655 return self._local_system_image_dir 656 657 @property 658 def local_tool_dirs(self): 659 """Return a list of local tool directories.""" 660 return self._local_tool_dirs 661 662 @property 663 def avd_type(self): 664 """Return the avd type.""" 665 return self._avd_type 666 667 @property 668 def autoconnect(self): 669 """autoconnect. 670 671 args.autoconnect could pass as Boolean or String. 672 673 Return: Boolean, True only if self._autoconnect is not False. 674 """ 675 return self._autoconnect is not False 676 677 @property 678 def connect_adb(self): 679 """Auto-connect to adb. 680 681 Return: Boolean, whether autoconnect is enabled. 682 """ 683 return self._autoconnect is not False 684 685 @property 686 def connect_vnc(self): 687 """Launch vnc. 688 689 Return: Boolean, True if self._autoconnect is 'vnc'. 690 """ 691 return self._autoconnect == constants.INS_KEY_VNC 692 693 @property 694 def connect_webrtc(self): 695 """Auto-launch webRTC AVD on the browser. 696 697 Return: Boolean, True if args.autoconnect is "webrtc". 698 """ 699 return self._autoconnect == constants.INS_KEY_WEBRTC 700 701 @property 702 def unlock_screen(self): 703 """Return unlock_screen.""" 704 return self._unlock_screen 705 706 @property 707 def remote_image(self): 708 """Return the remote image.""" 709 return self._remote_image 710 711 @property 712 def num(self): 713 """Return num of instances.""" 714 return self._num_of_instances 715 716 @property 717 def report_internal_ip(self): 718 """Return report internal ip.""" 719 return self._report_internal_ip 720 721 @property 722 def kernel_build_info(self): 723 """Return kernel build info.""" 724 return self._kernel_build_info 725 726 @property 727 def flavor(self): 728 """Return flavor.""" 729 return self._flavor 730 731 @property 732 def cfg(self): 733 """Return cfg instance.""" 734 return self._cfg 735 736 @property 737 def image_download_dir(self): 738 """Return image download dir.""" 739 return self._image_download_dir 740 741 @image_download_dir.setter 742 def image_download_dir(self, value): 743 """Set image download dir.""" 744 self._image_download_dir = value 745 746 @property 747 def serial_log_file(self): 748 """Return serial log file path.""" 749 return self._serial_log_file 750 751 @property 752 def gpu(self): 753 """Return gpu.""" 754 return self._gpu 755 756 @property 757 def emulator_build_id(self): 758 """Return emulator_build_id.""" 759 return self._emulator_build_id 760 761 @property 762 def client_adb_port(self): 763 """Return the client adb port.""" 764 return self._client_adb_port 765 766 @property 767 def stable_cheeps_host_image_name(self): 768 """Return the Cheeps host image name.""" 769 return self._stable_cheeps_host_image_name 770 771 # pylint: disable=invalid-name 772 @property 773 def stable_cheeps_host_image_project(self): 774 """Return the project hosting the Cheeps host image.""" 775 return self._stable_cheeps_host_image_project 776 777 @property 778 def username(self): 779 """Return username.""" 780 return self._username 781 782 @property 783 def password(self): 784 """Return password.""" 785 return self._password 786 787 @property 788 def boot_timeout_secs(self): 789 """Return boot_timeout_secs.""" 790 return self._boot_timeout_secs 791 792 @property 793 def ins_timeout_secs(self): 794 """Return ins_timeout_secs.""" 795 return self._ins_timeout_secs 796 797 @property 798 def system_build_info(self): 799 """Return system_build_info.""" 800 return self._system_build_info 801 802 @property 803 def local_instance_id(self): 804 """Return local_instance_id.""" 805 return self._local_instance_id 806 807 @property 808 def instance_name_to_reuse(self): 809 """Return instance_name_to_reuse.""" 810 return self._instance_name_to_reuse 811 812 @property 813 def remote_host(self): 814 """Return host.""" 815 return self._remote_host 816 817 @property 818 def host_user(self): 819 """Return host_user.""" 820 return self._host_user 821 822 @property 823 def host_ssh_private_key_path(self): 824 """Return host_ssh_private_key_path.""" 825 return self._host_ssh_private_key_path 826 827 @property 828 def no_pull_log(self): 829 """Return no_pull_log.""" 830 return self._no_pull_log 831