1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import contextlib 6import getpass 7import subprocess 8import os 9 10import common 11from autotest_lib.server.hosts import ssh_host 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.common_lib import global_config 14from autotest_lib.client.common_lib import utils 15 16 17@contextlib.contextmanager 18def chdir(dirname=None): 19 """A context manager to help change directories. 20 21 Will chdir into the provided dirname for the lifetime of the context and 22 return to cwd thereafter. 23 24 @param dirname: The dirname to chdir into. 25 """ 26 curdir = os.getcwd() 27 try: 28 if dirname is not None: 29 os.chdir(dirname) 30 yield 31 finally: 32 os.chdir(curdir) 33 34 35def local_runner(cmd, stream_output=False): 36 """ 37 Runs a command on the local system as the current user. 38 39 @param cmd: The command to run. 40 @param stream_output: If True, streams the stdout of the process. 41 42 @returns: The output of cmd, will be stdout and stderr. 43 @raises CalledProcessError: If there was a non-0 return code. 44 """ 45 print('Running command: %s' % cmd) 46 proc = subprocess.Popen( 47 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 48 if stream_output: 49 output = '' 50 for newline in iter(proc.stdout.readline, ''): 51 output += newline 52 print(newline.rstrip(os.linesep)) 53 else: 54 output = proc.communicate()[0] 55 56 return_code = proc.wait() 57 if return_code !=0: 58 print("ERROR: '%s' failed with error:\n%s" % (cmd, output)) 59 raise subprocess.CalledProcessError(return_code, cmd, output[:1024]) 60 return output 61 62 63_host_objects = {} 64 65def host_object_runner(host, **kwargs): 66 """ 67 Returns a function that returns the output of running a command via a host 68 object. 69 70 @param host: The host to run a command on. 71 @returns: A function that can invoke a command remotely. 72 """ 73 try: 74 host_object = _host_objects[host] 75 except KeyError: 76 username = global_config.global_config.get_config_value( 77 'CROS', 'infrastructure_user') 78 host_object = ssh_host.SSHHost(host, user=username) 79 _host_objects[host] = host_object 80 81 def runner(cmd): 82 """ 83 Runs a command via a host object on the enclosed host. Translates 84 host.run errors to the subprocess equivalent to expose a common API. 85 86 @param cmd: The command to run. 87 @returns: The output of cmd. 88 @raises CalledProcessError: If there was a non-0 return code. 89 """ 90 try: 91 return host_object.run(cmd).stdout 92 except error.AutotestHostRunError as e: 93 exit_status = e.result_obj.exit_status 94 command = e.result_obj.command 95 raise subprocess.CalledProcessError(exit_status, command) 96 return runner 97 98 99def googlesh_runner(host, **kwargs): 100 """ 101 Returns a function that return the output of running a command via shelling 102 out to `googlesh`. 103 104 @param host: The host to run a command on 105 @returns: A function that can invoke a command remotely. 106 """ 107 def runner(cmd): 108 """ 109 Runs a command via googlesh on the enclosed host. 110 111 @param cmd: The command to run. 112 @returns: The output of cmd. 113 @raises CalledProcessError: If there was a non-0 return code. 114 """ 115 out = subprocess.check_output(['googlesh', '-s', '-uchromeos-test', 116 '-m%s' % host, '%s' % cmd], 117 stderr=subprocess.STDOUT) 118 return out 119 return runner 120 121 122def execute_command(host, cmd, **kwargs): 123 """ 124 Executes a command on the host `host`. This an optimization that if 125 we're already chromeos-test, we can just ssh to the machine in question. 126 Or if we're local, we don't have to ssh at all. 127 128 @param host: The hostname to execute the command on. 129 @param cmd: The command to run. Special shell syntax (such as pipes) 130 is allowed. 131 @param kwargs: Key word arguments for the runner functions. 132 @returns: The output of the command. 133 """ 134 if utils.is_localhost(host): 135 runner = local_runner 136 elif getpass.getuser() == 'chromeos-test': 137 runner = host_object_runner(host) 138 else: 139 runner = googlesh_runner(host) 140 141 return runner(cmd, **kwargs) 142