1*800a58d9SAndroid Build Coastguard Worker# Copyright 2019 - The Android Open Source Project 2*800a58d9SAndroid Build Coastguard Worker# 3*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*800a58d9SAndroid Build Coastguard Worker# 7*800a58d9SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*800a58d9SAndroid Build Coastguard Worker# 9*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*800a58d9SAndroid Build Coastguard Worker# limitations under the License. 14*800a58d9SAndroid Build Coastguard Workerr"""Pull entry point. 15*800a58d9SAndroid Build Coastguard Worker 16*800a58d9SAndroid Build Coastguard WorkerThis command will pull the log files from a remote instance for AVD troubleshooting. 17*800a58d9SAndroid Build Coastguard Worker""" 18*800a58d9SAndroid Build Coastguard Worker 19*800a58d9SAndroid Build Coastguard Workerfrom __future__ import print_function 20*800a58d9SAndroid Build Coastguard Workerimport logging 21*800a58d9SAndroid Build Coastguard Workerimport os 22*800a58d9SAndroid Build Coastguard Workerimport tempfile 23*800a58d9SAndroid Build Coastguard Worker 24*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors 25*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants 26*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils 27*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.ssh import Ssh 28*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.ssh import IP 29*800a58d9SAndroid Build Coastguard Workerfrom acloud.list import list as list_instances 30*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import config 31*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import report 32*800a58d9SAndroid Build Coastguard Worker 33*800a58d9SAndroid Build Coastguard Worker 34*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 35*800a58d9SAndroid Build Coastguard Worker 36*800a58d9SAndroid Build Coastguard Worker# Black list for log files. 37*800a58d9SAndroid Build Coastguard Worker_KERNEL = "kernel" 38*800a58d9SAndroid Build Coastguard Worker_IMG_FILE_EXTENSION = ".img" 39*800a58d9SAndroid Build Coastguard Worker 40*800a58d9SAndroid Build Coastguard Worker 41*800a58d9SAndroid Build Coastguard Workerdef PullFileFromInstance(cfg, instance, file_name=None, no_prompts=False): 42*800a58d9SAndroid Build Coastguard Worker """Pull file from remote CF instance. 43*800a58d9SAndroid Build Coastguard Worker 44*800a58d9SAndroid Build Coastguard Worker 1. Download log files to temp folder. 45*800a58d9SAndroid Build Coastguard Worker 2. If only one file selected, display it on screen. 46*800a58d9SAndroid Build Coastguard Worker 3. Show the download folder for users. 47*800a58d9SAndroid Build Coastguard Worker 48*800a58d9SAndroid Build Coastguard Worker Args: 49*800a58d9SAndroid Build Coastguard Worker cfg: AcloudConfig object. 50*800a58d9SAndroid Build Coastguard Worker instance: list.Instance() object. 51*800a58d9SAndroid Build Coastguard Worker file_name: String of file name. 52*800a58d9SAndroid Build Coastguard Worker no_prompts: Boolean, True to skip the prompt about file streaming. 53*800a58d9SAndroid Build Coastguard Worker 54*800a58d9SAndroid Build Coastguard Worker Returns: 55*800a58d9SAndroid Build Coastguard Worker A Report instance. 56*800a58d9SAndroid Build Coastguard Worker """ 57*800a58d9SAndroid Build Coastguard Worker ssh = Ssh(ip=IP(ip=instance.ip), 58*800a58d9SAndroid Build Coastguard Worker user=constants.GCE_USER, 59*800a58d9SAndroid Build Coastguard Worker ssh_private_key_path=cfg.ssh_private_key_path, 60*800a58d9SAndroid Build Coastguard Worker extra_args_ssh_tunnel=cfg.extra_args_ssh_tunnel) 61*800a58d9SAndroid Build Coastguard Worker log_files = SelectLogFileToPull(ssh, file_name) 62*800a58d9SAndroid Build Coastguard Worker PullLogs(ssh, log_files, instance.name) 63*800a58d9SAndroid Build Coastguard Worker if len(log_files) == 1: 64*800a58d9SAndroid Build Coastguard Worker DisplayLog(ssh, log_files[0], no_prompts) 65*800a58d9SAndroid Build Coastguard Worker return report.Report(command="pull") 66*800a58d9SAndroid Build Coastguard Worker 67*800a58d9SAndroid Build Coastguard Worker 68*800a58d9SAndroid Build Coastguard Workerdef PullLogs(ssh, log_files, instance_name): 69*800a58d9SAndroid Build Coastguard Worker """Pull log files from remote instance. 70*800a58d9SAndroid Build Coastguard Worker 71*800a58d9SAndroid Build Coastguard Worker Args: 72*800a58d9SAndroid Build Coastguard Worker ssh: Ssh object. 73*800a58d9SAndroid Build Coastguard Worker log_files: List of file path in the remote instance. 74*800a58d9SAndroid Build Coastguard Worker instance_name: The instance name that is used to create the download 75*800a58d9SAndroid Build Coastguard Worker folder. 76*800a58d9SAndroid Build Coastguard Worker 77*800a58d9SAndroid Build Coastguard Worker Returns: 78*800a58d9SAndroid Build Coastguard Worker The download folder path. 79*800a58d9SAndroid Build Coastguard Worker """ 80*800a58d9SAndroid Build Coastguard Worker download_folder = _GetDownloadLogFolder(instance_name) 81*800a58d9SAndroid Build Coastguard Worker for log_file in log_files: 82*800a58d9SAndroid Build Coastguard Worker target_file = os.path.join(download_folder, os.path.basename(log_file)) 83*800a58d9SAndroid Build Coastguard Worker ssh.ScpPullFile(log_file, target_file) 84*800a58d9SAndroid Build Coastguard Worker _DisplayPullResult(download_folder) 85*800a58d9SAndroid Build Coastguard Worker return download_folder 86*800a58d9SAndroid Build Coastguard Worker 87*800a58d9SAndroid Build Coastguard Worker 88*800a58d9SAndroid Build Coastguard Workerdef DisplayLog(ssh, log_file, no_prompts=False): 89*800a58d9SAndroid Build Coastguard Worker """Display the content of log file in the screen. 90*800a58d9SAndroid Build Coastguard Worker 91*800a58d9SAndroid Build Coastguard Worker Args: 92*800a58d9SAndroid Build Coastguard Worker ssh: Ssh object. 93*800a58d9SAndroid Build Coastguard Worker log_file: String of the log file path. 94*800a58d9SAndroid Build Coastguard Worker no_prompts: Boolean, True to skip all prompts. 95*800a58d9SAndroid Build Coastguard Worker """ 96*800a58d9SAndroid Build Coastguard Worker warning_msg = ("It will stream log to show on screen. If you want to stop " 97*800a58d9SAndroid Build Coastguard Worker "streaming, please press CTRL-C to exit.\nPress 'y' to show " 98*800a58d9SAndroid Build Coastguard Worker "log or read log by myself[y/N]:") 99*800a58d9SAndroid Build Coastguard Worker if no_prompts or utils.GetUserAnswerYes(warning_msg): 100*800a58d9SAndroid Build Coastguard Worker ssh.Run("tail -f -n +1 %s" % log_file, show_output=True) 101*800a58d9SAndroid Build Coastguard Worker 102*800a58d9SAndroid Build Coastguard Worker 103*800a58d9SAndroid Build Coastguard Workerdef _DisplayPullResult(download_folder): 104*800a58d9SAndroid Build Coastguard Worker """Display messages to user after pulling log files. 105*800a58d9SAndroid Build Coastguard Worker 106*800a58d9SAndroid Build Coastguard Worker Args: 107*800a58d9SAndroid Build Coastguard Worker download_folder: String of download folder path. 108*800a58d9SAndroid Build Coastguard Worker """ 109*800a58d9SAndroid Build Coastguard Worker utils.PrintColorString( 110*800a58d9SAndroid Build Coastguard Worker "Download logs to folder: %s \nYou can look into log files to check " 111*800a58d9SAndroid Build Coastguard Worker "AVD issues." % download_folder) 112*800a58d9SAndroid Build Coastguard Worker 113*800a58d9SAndroid Build Coastguard Worker 114*800a58d9SAndroid Build Coastguard Workerdef _GetDownloadLogFolder(instance): 115*800a58d9SAndroid Build Coastguard Worker """Get the download log folder accroding to instance name. 116*800a58d9SAndroid Build Coastguard Worker 117*800a58d9SAndroid Build Coastguard Worker Args: 118*800a58d9SAndroid Build Coastguard Worker instance: String, the name of instance. 119*800a58d9SAndroid Build Coastguard Worker 120*800a58d9SAndroid Build Coastguard Worker Returns: 121*800a58d9SAndroid Build Coastguard Worker String of the download folder path. 122*800a58d9SAndroid Build Coastguard Worker """ 123*800a58d9SAndroid Build Coastguard Worker log_folder = os.path.join(tempfile.gettempdir(), instance) 124*800a58d9SAndroid Build Coastguard Worker if not os.path.exists(log_folder): 125*800a58d9SAndroid Build Coastguard Worker os.makedirs(log_folder) 126*800a58d9SAndroid Build Coastguard Worker logger.info("Download logs to folder: %s", log_folder) 127*800a58d9SAndroid Build Coastguard Worker return log_folder 128*800a58d9SAndroid Build Coastguard Worker 129*800a58d9SAndroid Build Coastguard Worker 130*800a58d9SAndroid Build Coastguard Workerdef SelectLogFileToPull(ssh, file_name=None): 131*800a58d9SAndroid Build Coastguard Worker """Select one log file or all log files to downalod. 132*800a58d9SAndroid Build Coastguard Worker 133*800a58d9SAndroid Build Coastguard Worker 1. Get all log file paths as selection list 134*800a58d9SAndroid Build Coastguard Worker 2. Get user selected file path or user provided file name. 135*800a58d9SAndroid Build Coastguard Worker 136*800a58d9SAndroid Build Coastguard Worker Args: 137*800a58d9SAndroid Build Coastguard Worker ssh: Ssh object. 138*800a58d9SAndroid Build Coastguard Worker file_name: String of file name. 139*800a58d9SAndroid Build Coastguard Worker 140*800a58d9SAndroid Build Coastguard Worker Returns: 141*800a58d9SAndroid Build Coastguard Worker List of selected file paths. 142*800a58d9SAndroid Build Coastguard Worker 143*800a58d9SAndroid Build Coastguard Worker Raises: 144*800a58d9SAndroid Build Coastguard Worker errors.CheckPathError: Can't find log files. 145*800a58d9SAndroid Build Coastguard Worker """ 146*800a58d9SAndroid Build Coastguard Worker log_files = GetAllLogFilePaths(ssh, constants.REMOTE_LOG_FOLDER) 147*800a58d9SAndroid Build Coastguard Worker if file_name: 148*800a58d9SAndroid Build Coastguard Worker file_path = os.path.join(constants.REMOTE_LOG_FOLDER, file_name) 149*800a58d9SAndroid Build Coastguard Worker if file_path in log_files: 150*800a58d9SAndroid Build Coastguard Worker return [file_path] 151*800a58d9SAndroid Build Coastguard Worker raise errors.CheckPathError("Can't find this log file(%s) from remote " 152*800a58d9SAndroid Build Coastguard Worker "instance." % file_path) 153*800a58d9SAndroid Build Coastguard Worker 154*800a58d9SAndroid Build Coastguard Worker if len(log_files) == 1: 155*800a58d9SAndroid Build Coastguard Worker return log_files 156*800a58d9SAndroid Build Coastguard Worker 157*800a58d9SAndroid Build Coastguard Worker if len(log_files) > 1: 158*800a58d9SAndroid Build Coastguard Worker print("Multiple log files detected, choose any one to proceed:") 159*800a58d9SAndroid Build Coastguard Worker return utils.GetAnswerFromList(log_files, enable_choose_all=True) 160*800a58d9SAndroid Build Coastguard Worker 161*800a58d9SAndroid Build Coastguard Worker raise errors.CheckPathError("Can't find any log file in folder(%s) from " 162*800a58d9SAndroid Build Coastguard Worker "remote instance." % constants.REMOTE_LOG_FOLDER) 163*800a58d9SAndroid Build Coastguard Worker 164*800a58d9SAndroid Build Coastguard Worker 165*800a58d9SAndroid Build Coastguard Workerdef GetAllLogFilePaths(ssh, remote_log_folder): 166*800a58d9SAndroid Build Coastguard Worker """Get all file paths under the log folder. 167*800a58d9SAndroid Build Coastguard Worker 168*800a58d9SAndroid Build Coastguard Worker Two rules to filter out files. 169*800a58d9SAndroid Build Coastguard Worker 1. File name is "kernel". 170*800a58d9SAndroid Build Coastguard Worker 2. File type is image "*.img". 171*800a58d9SAndroid Build Coastguard Worker 172*800a58d9SAndroid Build Coastguard Worker Args: 173*800a58d9SAndroid Build Coastguard Worker ssh: Ssh object. 174*800a58d9SAndroid Build Coastguard Worker remote_log_folder: The path to the remote log folder. 175*800a58d9SAndroid Build Coastguard Worker 176*800a58d9SAndroid Build Coastguard Worker Returns: 177*800a58d9SAndroid Build Coastguard Worker List of strings, the log file paths. 178*800a58d9SAndroid Build Coastguard Worker """ 179*800a58d9SAndroid Build Coastguard Worker files = utils.FindRemoteFiles(ssh, [remote_log_folder]) 180*800a58d9SAndroid Build Coastguard Worker log_files = list(files) 181*800a58d9SAndroid Build Coastguard Worker for file_path in files: 182*800a58d9SAndroid Build Coastguard Worker file_name = os.path.basename(file_path) 183*800a58d9SAndroid Build Coastguard Worker if file_name == _KERNEL or file_name.endswith(_IMG_FILE_EXTENSION): 184*800a58d9SAndroid Build Coastguard Worker log_files.remove(file_path) 185*800a58d9SAndroid Build Coastguard Worker return log_files 186*800a58d9SAndroid Build Coastguard Worker 187*800a58d9SAndroid Build Coastguard Worker 188*800a58d9SAndroid Build Coastguard Workerdef Run(args): 189*800a58d9SAndroid Build Coastguard Worker """Run pull. 190*800a58d9SAndroid Build Coastguard Worker 191*800a58d9SAndroid Build Coastguard Worker After pull command executed, tool will return one Report instance. 192*800a58d9SAndroid Build Coastguard Worker If there is no instance to pull, just return empty Report. 193*800a58d9SAndroid Build Coastguard Worker 194*800a58d9SAndroid Build Coastguard Worker Args: 195*800a58d9SAndroid Build Coastguard Worker args: Namespace object from argparse.parse_args. 196*800a58d9SAndroid Build Coastguard Worker 197*800a58d9SAndroid Build Coastguard Worker Returns: 198*800a58d9SAndroid Build Coastguard Worker A Report instance. 199*800a58d9SAndroid Build Coastguard Worker """ 200*800a58d9SAndroid Build Coastguard Worker cfg = config.GetAcloudConfig(args) 201*800a58d9SAndroid Build Coastguard Worker if args.instance_name: 202*800a58d9SAndroid Build Coastguard Worker instance = list_instances.GetInstancesFromInstanceNames( 203*800a58d9SAndroid Build Coastguard Worker cfg, [args.instance_name]) 204*800a58d9SAndroid Build Coastguard Worker return PullFileFromInstance(cfg, instance[0], args.file_name, args.no_prompt) 205*800a58d9SAndroid Build Coastguard Worker return PullFileFromInstance(cfg, 206*800a58d9SAndroid Build Coastguard Worker list_instances.ChooseOneRemoteInstance(cfg), 207*800a58d9SAndroid Build Coastguard Worker args.file_name, 208*800a58d9SAndroid Build Coastguard Worker args.no_prompt) 209