1# Copyright 2009 Google Inc. Released under the GPL v2 2 3""" 4This file contains the implementation of a host object for the local machine. 5""" 6 7import distutils.core, glob, os, platform, shutil 8from autotest_lib.client.common_lib import hosts, error 9from autotest_lib.client.bin import utils 10 11class LocalHost(hosts.Host): 12 """This class represents a host running locally on the host.""" 13 14 15 def _initialize(self, hostname=None, bootloader=None, *args, **dargs): 16 super(LocalHost, self)._initialize(*args, **dargs) 17 18 # hostname will be an actual hostname when this client was created 19 # by an autoserv process 20 if not hostname: 21 hostname = platform.node() 22 self.hostname = hostname 23 self.bootloader = bootloader 24 self.tmp_dirs = [] 25 26 27 def close(self): 28 """Cleanup after we're done.""" 29 for tmp_dir in self.tmp_dirs: 30 self.run('rm -rf "%s"' % (utils.sh_escape(tmp_dir)), 31 ignore_status=True) 32 33 34 def wait_up(self, timeout=None): 35 # a local host is always up 36 return True 37 38 39 def run(self, command, timeout=3600, ignore_status=False, 40 stdout_tee=utils.TEE_TO_LOGS, stderr_tee=utils.TEE_TO_LOGS, 41 stdin=None, args=(), **kwargs): 42 """ 43 @see common_lib.hosts.Host.run() 44 """ 45 try: 46 result = utils.run( 47 command, timeout=timeout, ignore_status=True, 48 stdout_tee=stdout_tee, stderr_tee=stderr_tee, stdin=stdin, 49 args=args) 50 except error.CmdError, e: 51 # this indicates a timeout exception 52 raise error.AutotestHostRunError('command timed out', e.result_obj) 53 54 if not ignore_status and result.exit_status > 0: 55 raise error.AutotestHostRunError('command execution error', result) 56 57 return result 58 59 60 def list_files_glob(self, path_glob): 61 """ 62 Get a list of files on a remote host given a glob pattern path. 63 """ 64 return glob.glob(path_glob) 65 66 67 def symlink_closure(self, paths): 68 """ 69 Given a sequence of path strings, return the set of all paths that 70 can be reached from the initial set by following symlinks. 71 72 @param paths: sequence of path strings. 73 @return: a sequence of path strings that are all the unique paths that 74 can be reached from the given ones after following symlinks. 75 """ 76 paths = set(paths) 77 closure = set() 78 79 while paths: 80 path = paths.pop() 81 if not os.path.exists(path): 82 continue 83 closure.add(path) 84 if os.path.islink(path): 85 link_to = os.path.join(os.path.dirname(path), 86 os.readlink(path)) 87 if link_to not in closure: 88 paths.add(link_to) 89 90 return closure 91 92 93 def _copy_file(self, source, dest, delete_dest=False, preserve_perm=False, 94 preserve_symlinks=False): 95 """Copy files from source to dest, will be the base for {get,send}_file. 96 97 @param source: The file/directory on localhost to copy. 98 @param dest: The destination path on localhost to copy to. 99 @param delete_dest: A flag set to choose whether or not to delete 100 dest if it exists. 101 @param preserve_perm: Tells get_file() to try to preserve the sources 102 permissions on files and dirs. 103 @param preserve_symlinks: Try to preserve symlinks instead of 104 transforming them into files/dirs on copy. 105 """ 106 if delete_dest and os.path.exists(dest): 107 # Check if it's a file or a dir and use proper remove method. 108 if os.path.isdir(dest): 109 shutil.rmtree(dest) 110 else: 111 os.remove(dest) 112 113 if preserve_symlinks and os.path.islink(source): 114 os.symlink(os.readlink(source), dest) 115 # If source is a dir, use distutils.dir_util.copytree since 116 # shutil.copy_tree has weird limitations. 117 elif os.path.isdir(source): 118 distutils.dir_util.copy_tree(source, dest, 119 preserve_symlinks=preserve_symlinks, 120 preserve_mode=preserve_perm, 121 update=1) 122 else: 123 shutil.copyfile(source, dest) 124 125 if preserve_perm: 126 shutil.copymode(source, dest) 127 128 129 def get_file(self, source, dest, delete_dest=False, preserve_perm=True, 130 preserve_symlinks=False): 131 """Copy files from source to dest. 132 133 @param source: The file/directory on localhost to copy. 134 @param dest: The destination path on localhost to copy to. 135 @param delete_dest: A flag set to choose whether or not to delete 136 dest if it exists. 137 @param preserve_perm: Tells get_file() to try to preserve the sources 138 permissions on files and dirs. 139 @param preserve_symlinks: Try to preserve symlinks instead of 140 transforming them into files/dirs on copy. 141 """ 142 self._copy_file(source, dest, delete_dest=delete_dest, 143 preserve_perm=preserve_perm, 144 preserve_symlinks=preserve_symlinks) 145 146 147 def send_file(self, source, dest, delete_dest=False, 148 preserve_symlinks=False): 149 """Copy files from source to dest. 150 151 @param source: The file/directory on the drone to send to the device. 152 @param dest: The destination path on the device to copy to. 153 @param delete_dest: A flag set to choose whether or not to delete 154 dest on the device if it exists. 155 @param preserve_symlinks: Controls if symlinks on the source will be 156 copied as such on the destination or 157 transformed into the referenced 158 file/directory. 159 """ 160 self._copy_file(source, dest, delete_dest=delete_dest, 161 preserve_symlinks=preserve_symlinks) 162 163 164 def get_tmp_dir(self, parent='/tmp'): 165 """ 166 Return the pathname of a directory on the host suitable 167 for temporary file storage. 168 169 The directory and its content will be deleted automatically 170 on the destruction of the Host object that was used to obtain 171 it. 172 173 @param parent: The leading path to make the tmp dir. 174 """ 175 self.run('mkdir -p "%s"' % parent) 176 tmp_dir = self.run('mktemp -d -p "%s"' % parent).stdout.rstrip() 177 self.tmp_dirs.append(tmp_dir) 178 return tmp_dir 179