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