def DeleteRemoteInstances(cfg, instances_to_delete, delete_report=None): """Delete remote instances. Args: cfg: AcloudConfig object. instances_to_delete: List of instance names(string). delete_report: Report object. Returns: Report instance if there are instances to delete, None otherwise. Raises: error.ConfigError: when config doesn't support remote instances. """ if not cfg.SupportRemoteInstance(): raise errors.ConfigError("No gcp project info found in config! " "The execution of deleting remote instances " "has been aborted.") utils.PrintColorString("") for instance in instances_to_delete: utils.PrintColorString(" - %s" % instance, utils.TextColors.WARNING) utils.PrintColorString("") utils.PrintColorString("status: waiting...", end="") # TODO(119283708): We should move DeleteAndroidVirtualDevices into # delete.py after gce is deprecated. # Stop remote instances. delete_report = device_driver.DeleteAndroidVirtualDevices( cfg, instances_to_delete, delete_report) return delete_report
def _DisconnectAndRaiseError(self): """Disconnect adb. Disconnect from the device's network address if it shows up in adb devices. For example, adb disconnect 127.0.0.1:5555. Raises: errors.WaitForAdbDieError: adb is alive after disconnect adb. """ try: if self.IsAdbConnected(): adb_disconnect_args = [self._adb_command, _ADB_DISCONNECT, self._device_address] subprocess.check_call(adb_disconnect_args) # check adb device status self._GetAdbInformation() if self.IsAdbConnected(): raise errors.AdbDisconnectFailed( "adb disconnect failed, device is still connected and " "has status: [%s]" % self.GetAdbConnectionStatus()) except subprocess.CalledProcessError: utils.PrintColorString("Failed to adb disconnect %s" % self._device_address, utils.TextColors.FAIL)
def Run(args): """Run reconnect. Args: args: Namespace object from argparse.parse_args. """ cfg = config.GetAcloudConfig(args) instances_to_reconnect = [] if args.instance_names is not None: # user input instance name to get instance object. instances_to_reconnect = list_instance.GetInstancesFromInstanceNames( cfg, args.instance_names) if not instances_to_reconnect: instances_to_reconnect = list_instance.ChooseInstances(cfg, args.all) reconnect_report = report.Report(command="reconnect") for instance in instances_to_reconnect: if instance.avd_type not in utils.AVD_PORT_DICT: utils.PrintColorString( "Skipping reconnect of instance %s due to " "unknown avd type (%s)." % (instance.name, instance.avd_type), utils.TextColors.WARNING) continue if not instance.islocal: AddPublicSshRsaToInstance(cfg, constants.GCE_USER, instance.name) ReconnectInstance(cfg.ssh_private_key_path, instance, reconnect_report, cfg.extra_args_ssh_tunnel, connect_vnc=(args.autoconnect is True)) utils.PrintDeviceSummary(reconnect_report)
def _DisplayPullResult(download_folder): """Display messages to user after pulling log files. Args: download_folder: String of download folder path. """ utils.PrintColorString( "Download logs to folder: %s \nYou can look into log files to check " "AVD issues." % download_folder)
def _CheckForAutoconnect(args): """Check that we have all prerequisites for autoconnect. Autoconnect requires adb and ssh, we'll just check for adb for now and assume ssh is everywhere. If adb isn't around, ask the user if they want us to build it, if not we'll disable autoconnect. Args: args: Namespace object from argparse.parse_args. """ if not args.autoconnect or utils.FindExecutable(constants.ADB_BIN): return disable_autoconnect = False answer = _YES if args.no_prompt else utils.InteractWithQuestion( "adb is required for autoconnect, without it autoconnect will be " "disabled, would you like acloud to build it[y/N]? ") if answer in constants.USER_ANSWER_YES: utils.PrintColorString("Building adb ... ", end="") android_build_top = os.environ.get(constants.ENV_ANDROID_BUILD_TOP) if not android_build_top: utils.PrintColorString("Fail! (Not in a lunch'd env)", utils.TextColors.FAIL) disable_autoconnect = True else: make_cmd = os.path.join(android_build_top, _MAKE_CMD) build_adb_cmd = [make_cmd, _MAKE_ARG, "adb"] try: with open(os.devnull, "w") as dev_null: subprocess.check_call(build_adb_cmd, stderr=dev_null, stdout=dev_null) utils.PrintColorString("OK!", utils.TextColors.OKGREEN) except subprocess.CalledProcessError: utils.PrintColorString("Fail! (build failed)", utils.TextColors.FAIL) disable_autoconnect = True else: disable_autoconnect = True if disable_autoconnect: utils.PrintColorString("Disabling autoconnect", utils.TextColors.WARNING) args.autoconnect = False
def PowerwashDevice(ssh, instance_id): """Powerwash AVD with the instance id. Args: ssh: Ssh object. instance_id: Integer of the instance id. """ ssh_command = "./bin/powerwash_cvd --instance_num=%d" % (instance_id) try: ssh.Run(ssh_command) except (subprocess.CalledProcessError, errors.DeviceConnectionError) as e: logger.debug(str(e)) utils.PrintColorString(str(e), utils.TextColors.FAIL)
def _ProcessCFLocalImageArgs(self, local_image_arg, flavor_arg): """Get local built image path for cuttlefish-type AVD. Two scenarios of using --local-image: - Without a following argument Set flavor string if the required images are in $ANDROID_PRODUCT_OUT, - With a following filename/dirname Set flavor string from the specified image/dir name. Args: local_image_arg: String of local image args. flavor_arg: String of flavor arg """ flavor_from_build_string = None if not local_image_arg: self._CheckCFBuildTarget(self._instance_type) local_image_path = utils.GetBuildEnvironmentVariable( _ENV_ANDROID_PRODUCT_OUT) else: local_image_path = local_image_arg if os.path.isfile(local_image_path): self._local_image_artifact = local_image_arg flavor_from_build_string = self._GetFlavorFromString( self._local_image_artifact) # Since file is provided and I assume it's a zip, so print the # warning message. utils.PrintColorString(_LOCAL_ZIP_WARNING_MSG, utils.TextColors.WARNING) else: self._local_image_dir = local_image_path # Since dir is provided, so checking that any images exist to ensure # user didn't forget to 'make' before launch AVD. image_list = glob.glob(os.path.join(self.local_image_dir, "*.img")) if not image_list: raise errors.GetLocalImageError( "No image found(Did you choose a lunch target and run `m`?)" ": %s.\n " % self.local_image_dir) try: flavor_from_build_string = self._GetFlavorFromString( utils.GetBuildEnvironmentVariable( constants.ENV_BUILD_TARGET)) except errors.GetAndroidBuildEnvVarError: logger.debug( "Unable to determine flavor from env variable: %s", constants.ENV_BUILD_TARGET) if flavor_from_build_string and not flavor_arg: self._flavor = flavor_from_build_string
def AutoUnlockScreen(self): """Auto unlock screen. Auto unlock screen after invoke vnc client. """ try: adb_unlock_args = _UNLOCK_SCREEN_KEYEVENT % { "adb_bin": self._adb_command, "device_serial": self._device_serial} subprocess.check_call(adb_unlock_args.split()) except subprocess.CalledProcessError: utils.PrintColorString("Failed to unlock screen." "(adb_port: %s)" % self._adb_port, utils.TextColors.WARNING)
def LaunchCvd(self, instance, avd_spec=None, blank_data_disk_size_gb=None, kernel_build=None, decompress_kernel=None, boot_timeout_secs=None): """Launch CVD. Launch AVD with launch_cvd. If the process is failed, acloud would show error messages and auto download log files from remote instance. Args: instance: String, instance name. avd_spec: An AVDSpec instance. blank_data_disk_size_gb: Size of the blank data disk in GB. kernel_build: String, kernel build info. decompress_kernel: Boolean, if true decompress the kernel. boot_timeout_secs: Integer, the maximum time to wait for the command to respond. Returns: dict of faliures, return this dict for BootEvaluator to handle LaunchCvd success or fail messages. """ timestart = time.time() error_msg = "" launch_cvd_args = self._GetLaunchCvdArgs(avd_spec, blank_data_disk_size_gb, kernel_build, decompress_kernel) boot_timeout_secs = boot_timeout_secs or constants.DEFAULT_CF_BOOT_TIMEOUT ssh_command = "./bin/launch_cvd -daemon " + " ".join(launch_cvd_args) try: self.ExtendReportData(_LAUNCH_CVD_COMMAND, ssh_command) self._ssh.Run(ssh_command, boot_timeout_secs, retry=_NO_RETRY) except (subprocess.CalledProcessError, errors.DeviceConnectionError) as e: # TODO(b/140475060): Distinguish the error is command return error # or timeout error. error_msg = ( "Device %s did not finish on boot within timeout (%s secs)" % (instance, boot_timeout_secs)) self._all_failures[instance] = error_msg utils.PrintColorString(str(e), utils.TextColors.FAIL) if avd_spec and not avd_spec.no_pull_log: self._PullAllLogFiles(instance) self._execution_time[_LAUNCH_CVD] = round(time.time() - timestart, 2) return {instance: error_msg} if error_msg else {}
def ConnectAdb(self): """Connect adb. Connect adb to the device's network address if the connection is not alive. For example, adb connect 127.0.0.1:5555. """ try: if not self.IsAdbConnectionAlive(): adb_connect_args = [self._adb_command, _ADB_CONNECT, self._device_address] subprocess.check_call(adb_connect_args) except subprocess.CalledProcessError: utils.PrintColorString("Failed to adb connect %s" % self._device_address, utils.TextColors.FAIL)
def ShowFailMessages(self, error): """Show fail messages. Show the fail messages to hint users the impact if the api service isn't enabled. Args: error: String of error message when opening api service failed. """ msg_color = (utils.TextColors.FAIL if self._required else utils.TextColors.WARNING) utils.PrintColorString( error + _OPEN_SERVICE_FAILED_MSG % { "service_name": self._name, "service_msg": self._error_msg }, msg_color)
def _CheckCFBuildTarget(instance_type): """Check build target for the given instance type Args: instance_type: String of instance type Raises: errors.GetLocalImageError if the pattern is not match with current build target. """ build_target = utils.GetBuildEnvironmentVariable( constants.ENV_BUILD_TARGET) pattern = constants.CF_AVD_BUILD_TARGET_PATTERN_MAPPING[instance_type] if pattern not in build_target: utils.PrintColorString( "%s is not a %s target (Try lunching a proper cuttlefish " "target and running 'm')" % (build_target, pattern), utils.TextColors.WARNING)
def PrintInstancesDetails(instance_list, verbose=False): """Display instances information. Example of non-verbose case: [1]device serial: 127.0.0.1:55685 (ins-1ff036dc-5128057-cf-x86-phone-userdebug) [2]device serial: 127.0.0.1:60979 (ins-80952669-5128057-cf-x86-phone-userdebug) [3]device serial: 127.0.0.1:6520 (local-instance) Example of verbose case: [1] name: ins-244710f0-5091715-aosp-cf-x86-phone-userdebug IP: None create time: 2018-10-25T06:32:08.182-07:00 status: TERMINATED avd type: cuttlefish display: 1080x1920 (240) [2] name: ins-82979192-5091715-aosp-cf-x86-phone-userdebug IP: 35.232.77.15 adb serial: 127.0.0.1:33537 create time: 2018-10-25T06:34:22.716-07:00 status: RUNNING avd type: cuttlefish display: 1080x1920 (240) Args: verbose: Boolean, True to print all details and only full name if False. instance_list: List of instances. """ if not instance_list: print("No remote or local instances found") for num, instance_info in enumerate(instance_list, 1): idx_str = "[%d]" % num utils.PrintColorString(idx_str, end="") if verbose: print(instance_info.Summary()) # add space between instances in verbose mode. print("") else: print(instance_info)
def _GetBranchFromRepo(self): """Get branch information from command "repo info". If branch can't get from "repo info", it will be set as default branch "aosp-master". Returns: branch: String, git branch name. e.g. "aosp-master" """ branch = None # TODO(149460014): Migrate acloud to py3, then remove this # workaround. env = os.environ.copy() env.pop("PYTHONPATH", None) logger.info("Running command \"%s\"", _COMMAND_REPO_INFO) # TODO(154173071): Migrate acloud to py3, then apply Popen to append with encoding process = subprocess.Popen(_COMMAND_REPO_INFO, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, universal_newlines=True) timer = threading.Timer(_REPO_TIMEOUT, process.kill) timer.start() stdout, _ = process.communicate() if stdout: for line in stdout.splitlines(): match = _BRANCH_RE.match(EscapeAnsi(line)) if match: branch_prefix = _BRANCH_PREFIX.get(self._GetGitRemote(), _DEFAULT_BRANCH_PREFIX) branch = branch_prefix + match.group("branch") timer.cancel() if branch: return branch utils.PrintColorString( "Unable to determine your repo branch, defaulting to %s" % _DEFAULT_BRANCH, utils.TextColors.WARNING) return _DEFAULT_BRANCH
def _ProcessFVPLocalImageArgs(self, local_image_arg): """Get local built image path for FVP-type AVD. Args: local_image_arg: String of local image args. """ build_target = utils.GetBuildEnvironmentVariable( constants.ENV_BUILD_TARGET) if build_target != "fvp": utils.PrintColorString( "%s is not an fvp target (Try lunching fvp-eng " "and running 'm')" % build_target, utils.TextColors.WARNING) self._local_image_dir = utils.GetBuildEnvironmentVariable( _ENV_ANDROID_PRODUCT_OUT) # Since dir is provided, so checking that any images exist to ensure # user didn't forget to 'make' before launch AVD. image_list = glob.glob(os.path.join(self.local_image_dir, "*.img")) if not image_list: raise errors.GetLocalImageError( "No image found(Did you choose a lunch target and run `m`?)" ": %s.\n " % self._local_image_dir)
def DeleteInstanceByNames(cfg, instances): """Delete instances by the names of these instances. Args: cfg: AcloudConfig object. instances: List of instance name. Returns: A Report instance. """ delete_report = report.Report(command="delete") local_instances = [ ins for ins in instances if ins.startswith(_LOCAL_INSTANCE_PREFIX) ] remote_instances = list(set(instances) - set(local_instances)) if local_instances: utils.PrintColorString("Deleting local instances") delete_report = DeleteInstances( cfg, list_instances.GetLocalInstancesByNames(local_instances)) if remote_instances: delete_report = DeleteRemoteInstances(cfg, remote_instances, delete_report) return delete_report
def PrintAvdDetails(avd_spec): """Display spec information to user. Example: Creating remote AVD instance with the following details: Image: aosp/master - aosp_cf_x86_phone-userdebug [1234] hw config: cpu - 2 ram - 2GB disk - 10GB display - 1024x862 (224 DPI) Args: avd_spec: AVDSpec object that tells us what we're going to create. """ utils.PrintColorString( "Creating %s AVD instance with the following details:" % avd_spec.instance_type) if avd_spec.image_source == constants.IMAGE_SRC_LOCAL: utils.PrintColorString("Image (local):") utils.PrintColorString( " %s" % (avd_spec.local_image_dir or avd_spec.local_image_artifact)) elif avd_spec.image_source == constants.IMAGE_SRC_REMOTE: utils.PrintColorString("Image:") utils.PrintColorString( " %s - %s [%s]" % (avd_spec.remote_image[constants.BUILD_BRANCH], avd_spec.remote_image[constants.BUILD_TARGET], avd_spec.remote_image[constants.BUILD_ID])) utils.PrintColorString("hw config:") utils.PrintColorString(" cpu - %s" % (avd_spec.hw_property[constants.HW_ALIAS_CPUS])) utils.PrintColorString( " ram - %dGB" % (int(avd_spec.hw_property[constants.HW_ALIAS_MEMORY]) / 1024)) if constants.HW_ALIAS_DISK in avd_spec.hw_property: utils.PrintColorString( " disk - %dGB" % (int(avd_spec.hw_property[constants.HW_ALIAS_DISK]) / 1024)) utils.PrintColorString(" display - %sx%s (%s DPI)" % (avd_spec.hw_property[constants.HW_X_RES], avd_spec.hw_property[constants.HW_Y_RES], avd_spec.hw_property[constants.HW_ALIAS_DPI])) utils.PrintColorString("\n")
def _PrintUsage(): """Print cmd usage hints when acloud setup been finished.""" utils.PrintColorString("") utils.PrintColorString("Setup process finished")