| # Copyright 2009 Google Inc. Released under the GPL v2 |
| |
| """ |
| This file contains the implementation of a host object for the local machine. |
| """ |
| |
| import distutils.core, glob, os, platform, shutil |
| from autotest_lib.client.common_lib import hosts, error |
| from autotest_lib.client.bin import utils |
| |
| class LocalHost(hosts.Host): |
| """This class represents a host running locally on the host.""" |
| |
| |
| def _initialize(self, hostname=None, bootloader=None, *args, **dargs): |
| super(LocalHost, self)._initialize(*args, **dargs) |
| |
| # hostname will be an actual hostname when this client was created |
| # by an autoserv process |
| if not hostname: |
| hostname = platform.node() |
| self.hostname = hostname |
| self.bootloader = bootloader |
| self.tmp_dirs = [] |
| |
| |
| def close(self): |
| """Cleanup after we're done.""" |
| for tmp_dir in self.tmp_dirs: |
| self.run('rm -rf "%s"' % (utils.sh_escape(tmp_dir)), |
| ignore_status=True) |
| |
| |
| def wait_up(self, timeout=None): |
| # a local host is always up |
| return True |
| |
| |
| def run(self, command, timeout=3600, ignore_status=False, |
| ignore_timeout=False, |
| stdout_tee=utils.TEE_TO_LOGS, stderr_tee=utils.TEE_TO_LOGS, |
| stdin=None, args=(), **kwargs): |
| """ |
| @see common_lib.hosts.Host.run() |
| """ |
| try: |
| result = utils.run( |
| command, timeout=timeout, ignore_status=True, |
| ignore_timeout=ignore_timeout, |
| stdout_tee=stdout_tee, stderr_tee=stderr_tee, stdin=stdin, |
| args=args) |
| except error.CmdError, e: |
| # this indicates a timeout exception |
| raise error.AutotestHostRunError('command timed out', e.result_obj) |
| |
| if ignore_timeout and result is None: |
| # We have timed out, there is no result to report. |
| return None |
| |
| if not ignore_status and result.exit_status > 0: |
| raise error.AutotestHostRunError('command execution error', result) |
| |
| return result |
| |
| |
| def list_files_glob(self, path_glob): |
| """ |
| Get a list of files on a remote host given a glob pattern path. |
| """ |
| return glob.glob(path_glob) |
| |
| |
| def symlink_closure(self, paths): |
| """ |
| Given a sequence of path strings, return the set of all paths that |
| can be reached from the initial set by following symlinks. |
| |
| @param paths: sequence of path strings. |
| @return: a sequence of path strings that are all the unique paths that |
| can be reached from the given ones after following symlinks. |
| """ |
| paths = set(paths) |
| closure = set() |
| |
| while paths: |
| path = paths.pop() |
| if not os.path.exists(path): |
| continue |
| closure.add(path) |
| if os.path.islink(path): |
| link_to = os.path.join(os.path.dirname(path), |
| os.readlink(path)) |
| if link_to not in closure: |
| paths.add(link_to) |
| |
| return closure |
| |
| |
| def _copy_file(self, source, dest, delete_dest=False, preserve_perm=False, |
| preserve_symlinks=False): |
| """Copy files from source to dest, will be the base for {get,send}_file. |
| |
| @param source: The file/directory on localhost to copy. |
| @param dest: The destination path on localhost to copy to. |
| @param delete_dest: A flag set to choose whether or not to delete |
| dest if it exists. |
| @param preserve_perm: Tells get_file() to try to preserve the sources |
| permissions on files and dirs. |
| @param preserve_symlinks: Try to preserve symlinks instead of |
| transforming them into files/dirs on copy. |
| """ |
| if delete_dest and os.path.exists(dest): |
| # Check if it's a file or a dir and use proper remove method. |
| if os.path.isdir(dest): |
| shutil.rmtree(dest) |
| else: |
| os.remove(dest) |
| |
| if preserve_symlinks and os.path.islink(source): |
| os.symlink(os.readlink(source), dest) |
| # If source is a dir, use distutils.dir_util.copytree since |
| # shutil.copy_tree has weird limitations. |
| elif os.path.isdir(source): |
| distutils.dir_util.copy_tree(source, dest, |
| preserve_symlinks=preserve_symlinks, |
| preserve_mode=preserve_perm, |
| update=1) |
| else: |
| shutil.copyfile(source, dest) |
| |
| if preserve_perm: |
| shutil.copymode(source, dest) |
| |
| |
| def get_file(self, source, dest, delete_dest=False, preserve_perm=True, |
| preserve_symlinks=False): |
| """Copy files from source to dest. |
| |
| @param source: The file/directory on localhost to copy. |
| @param dest: The destination path on localhost to copy to. |
| @param delete_dest: A flag set to choose whether or not to delete |
| dest if it exists. |
| @param preserve_perm: Tells get_file() to try to preserve the sources |
| permissions on files and dirs. |
| @param preserve_symlinks: Try to preserve symlinks instead of |
| transforming them into files/dirs on copy. |
| """ |
| self._copy_file(source, dest, delete_dest=delete_dest, |
| preserve_perm=preserve_perm, |
| preserve_symlinks=preserve_symlinks) |
| |
| |
| def send_file(self, source, dest, delete_dest=False, |
| preserve_symlinks=False): |
| """Copy files from source to dest. |
| |
| @param source: The file/directory on the drone to send to the device. |
| @param dest: The destination path on the device to copy to. |
| @param delete_dest: A flag set to choose whether or not to delete |
| dest on the device if it exists. |
| @param preserve_symlinks: Controls if symlinks on the source will be |
| copied as such on the destination or |
| transformed into the referenced |
| file/directory. |
| """ |
| self._copy_file(source, dest, delete_dest=delete_dest, |
| preserve_symlinks=preserve_symlinks) |
| |
| |
| def get_tmp_dir(self, parent='/tmp'): |
| """ |
| Return the pathname of a directory on the host suitable |
| for temporary file storage. |
| |
| The directory and its content will be deleted automatically |
| on the destruction of the Host object that was used to obtain |
| it. |
| |
| @param parent: The leading path to make the tmp dir. |
| """ |
| self.run('mkdir -p "%s"' % parent) |
| tmp_dir = self.run('mktemp -d -p "%s"' % parent).stdout.rstrip() |
| self.tmp_dirs.append(tmp_dir) |
| return tmp_dir |