1 # Copyright 2009 Google Inc. Released under the GPL v2 2 3 """ 4 This file contains the implementation of a host object for the local machine. 5 """ 6 7 import distutils.core, glob, os, platform, shutil 8 from autotest_lib.client.common_lib import hosts, error 9 from autotest_lib.client.bin import utils 10 11 class 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 ignore_timeout=False, 41 stdout_tee=utils.TEE_TO_LOGS, stderr_tee=utils.TEE_TO_LOGS, 42 stdin=None, args=(), **kwargs): 43 """ 44 @see common_lib.hosts.Host.run() 45 """ 46 try: 47 result = utils.run( 48 command, timeout=timeout, ignore_status=True, 49 ignore_timeout=ignore_timeout, 50 stdout_tee=stdout_tee, stderr_tee=stderr_tee, stdin=stdin, 51 args=args) 52 except error.CmdError, e: 53 # this indicates a timeout exception 54 raise error.AutotestHostRunError('command timed out', e.result_obj) 55 56 if ignore_timeout and result is None: 57 # We have timed out, there is no result to report. 58 return None 59 60 if not ignore_status and result.exit_status > 0: 61 raise error.AutotestHostRunError('command execution error', result) 62 63 return result 64 65 66 def list_files_glob(self, path_glob): 67 """ 68 Get a list of files on a remote host given a glob pattern path. 69 """ 70 return glob.glob(path_glob) 71 72 73 def symlink_closure(self, paths): 74 """ 75 Given a sequence of path strings, return the set of all paths that 76 can be reached from the initial set by following symlinks. 77 78 @param paths: sequence of path strings. 79 @return: a sequence of path strings that are all the unique paths that 80 can be reached from the given ones after following symlinks. 81 """ 82 paths = set(paths) 83 closure = set() 84 85 while paths: 86 path = paths.pop() 87 if not os.path.exists(path): 88 continue 89 closure.add(path) 90 if os.path.islink(path): 91 link_to = os.path.join(os.path.dirname(path), 92 os.readlink(path)) 93 if link_to not in closure: 94 paths.add(link_to) 95 96 return closure 97 98 99 def _copy_file(self, source, dest, delete_dest=False, preserve_perm=False, 100 preserve_symlinks=False): 101 """Copy files from source to dest, will be the base for {get,send}_file. 102 103 If source is a directory and ends with a trailing slash, only the 104 contents of the source directory will be copied to dest, otherwise 105 source itself will be copied under dest. 106 107 @param source: The file/directory on localhost to copy. 108 @param dest: The destination path on localhost to copy to. 109 @param delete_dest: A flag set to choose whether or not to delete 110 dest if it exists. 111 @param preserve_perm: Tells get_file() to try to preserve the sources 112 permissions on files and dirs. 113 @param preserve_symlinks: Try to preserve symlinks instead of 114 transforming them into files/dirs on copy. 115 """ 116 # We copy dest under source if either: 117 # 1. Source is a directory and doesn't end with /. 118 # 2. Source is a file and dest is a directory. 119 source_is_dir = os.path.isdir(source) 120 if ((source_is_dir and not source.endswith(os.sep)) or 121 (not source_is_dir and os.path.isdir(dest))): 122 dest = os.path.join(dest, os.path.basename(source)) 123 124 if delete_dest and os.path.exists(dest): 125 # Check if it's a file or a dir and use proper remove method. 126 if os.path.isdir(dest): 127 shutil.rmtree(dest) 128 os.mkdir(dest) 129 else: 130 os.remove(dest) 131 132 if preserve_symlinks and os.path.islink(source): 133 os.symlink(os.readlink(source), dest) 134 # If source is a dir, use distutils.dir_util.copytree since 135 # shutil.copy_tree has weird limitations. 136 elif os.path.isdir(source): 137 distutils.dir_util.copy_tree(source, dest, 138 preserve_symlinks=preserve_symlinks, 139 preserve_mode=preserve_perm, 140 update=1) 141 else: 142 shutil.copyfile(source, dest) 143 144 if preserve_perm: 145 shutil.copymode(source, dest) 146 147 148 def get_file(self, source, dest, delete_dest=False, preserve_perm=True, 149 preserve_symlinks=False): 150 """Copy files from source to dest. 151 152 If source is a directory and ends with a trailing slash, only the 153 contents of the source directory will be copied to dest, otherwise 154 source itself will be copied under dest. This is to match the 155 behavior of AbstractSSHHost.get_file(). 156 157 @param source: The file/directory on localhost to copy. 158 @param dest: The destination path on localhost to copy to. 159 @param delete_dest: A flag set to choose whether or not to delete 160 dest if it exists. 161 @param preserve_perm: Tells get_file() to try to preserve the sources 162 permissions on files and dirs. 163 @param preserve_symlinks: Try to preserve symlinks instead of 164 transforming them into files/dirs on copy. 165 """ 166 self._copy_file(source, dest, delete_dest=delete_dest, 167 preserve_perm=preserve_perm, 168 preserve_symlinks=preserve_symlinks) 169 170 171 def send_file(self, source, dest, delete_dest=False, 172 preserve_symlinks=False): 173 """Copy files from source to dest. 174 175 If source is a directory and ends with a trailing slash, only the 176 contents of the source directory will be copied to dest, otherwise 177 source itself will be copied under dest. This is to match the 178 behavior of AbstractSSHHost.send_file(). 179 180 @param source: The file/directory on the drone to send to the device. 181 @param dest: The destination path on the device to copy to. 182 @param delete_dest: A flag set to choose whether or not to delete 183 dest on the device if it exists. 184 @param preserve_symlinks: Controls if symlinks on the source will be 185 copied as such on the destination or 186 transformed into the referenced 187 file/directory. 188 """ 189 self._copy_file(source, dest, delete_dest=delete_dest, 190 preserve_symlinks=preserve_symlinks) 191 192 193 def get_tmp_dir(self, parent='/tmp'): 194 """ 195 Return the pathname of a directory on the host suitable 196 for temporary file storage. 197 198 The directory and its content will be deleted automatically 199 on the destruction of the Host object that was used to obtain 200 it. 201 202 @param parent: The leading path to make the tmp dir. 203 """ 204 self.run('mkdir -p "%s"' % parent) 205 tmp_dir = self.run('mktemp -d -p "%s"' % parent).stdout.rstrip() 206 self.tmp_dirs.append(tmp_dir) 207 return tmp_dir 208