Home | History | Annotate | Download | only in bin
      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