• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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            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