local_host.py revision 497780908dd4b33d966566f24830194a557eed4e
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        @param source: The file/directory on localhost to copy.
104        @param dest: The destination path on localhost to copy to.
105        @param delete_dest: A flag set to choose whether or not to delete
106                            dest if it exists.
107        @param preserve_perm: Tells get_file() to try to preserve the sources
108                              permissions on files and dirs.
109        @param preserve_symlinks: Try to preserve symlinks instead of
110                                  transforming them into files/dirs on copy.
111        """
112        if delete_dest and os.path.exists(dest):
113            # Check if it's a file or a dir and use proper remove method.
114            if os.path.isdir(dest):
115                shutil.rmtree(dest)
116            else:
117                os.remove(dest)
118
119        if preserve_symlinks and os.path.islink(source):
120            os.symlink(os.readlink(source), dest)
121        # If source is a dir, use distutils.dir_util.copytree since
122        # shutil.copy_tree has weird limitations.
123        elif os.path.isdir(source):
124            distutils.dir_util.copy_tree(source, dest,
125                    preserve_symlinks=preserve_symlinks,
126                    preserve_mode=preserve_perm,
127                    update=1)
128        else:
129            shutil.copyfile(source, dest)
130
131        if preserve_perm:
132            shutil.copymode(source, dest)
133
134
135    def get_file(self, source, dest, delete_dest=False, preserve_perm=True,
136                 preserve_symlinks=False):
137        """Copy files from source to dest.
138
139        @param source: The file/directory on localhost to copy.
140        @param dest: The destination path on localhost to copy to.
141        @param delete_dest: A flag set to choose whether or not to delete
142                            dest if it exists.
143        @param preserve_perm: Tells get_file() to try to preserve the sources
144                              permissions on files and dirs.
145        @param preserve_symlinks: Try to preserve symlinks instead of
146                                  transforming them into files/dirs on copy.
147        """
148        self._copy_file(source, dest, delete_dest=delete_dest,
149                        preserve_perm=preserve_perm,
150                        preserve_symlinks=preserve_symlinks)
151
152
153    def send_file(self, source, dest, delete_dest=False,
154                  preserve_symlinks=False):
155        """Copy files from source to dest.
156
157        @param source: The file/directory on the drone to send to the device.
158        @param dest: The destination path on the device to copy to.
159        @param delete_dest: A flag set to choose whether or not to delete
160                            dest on the device if it exists.
161        @param preserve_symlinks: Controls if symlinks on the source will be
162                                  copied as such on the destination or
163                                  transformed into the referenced
164                                  file/directory.
165        """
166        self._copy_file(source, dest, delete_dest=delete_dest,
167                        preserve_symlinks=preserve_symlinks)
168
169
170    def get_tmp_dir(self, parent='/tmp'):
171        """
172        Return the pathname of a directory on the host suitable
173        for temporary file storage.
174
175        The directory and its content will be deleted automatically
176        on the destruction of the Host object that was used to obtain
177        it.
178
179        @param parent: The leading path to make the tmp dir.
180        """
181        self.run('mkdir -p "%s"' % parent)
182        tmp_dir = self.run('mktemp -d -p "%s"' % parent).stdout.rstrip()
183        self.tmp_dirs.append(tmp_dir)
184        return tmp_dir
185