xref: /aosp_15_r20/external/autotest/server/hosts/teststation_host.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Li"""This class defines the TestStationHost class."""
6*9c5db199SXin Li
7*9c5db199SXin Liimport logging
8*9c5db199SXin Liimport os
9*9c5db199SXin Li
10*9c5db199SXin Liimport common
11*9c5db199SXin Li
12*9c5db199SXin Lifrom autotest_lib.client.bin import local_host
13*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
14*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import retry
15*9c5db199SXin Lifrom autotest_lib.client.cros import constants as cros_constants
16*9c5db199SXin Lifrom autotest_lib.server.hosts import base_classes
17*9c5db199SXin Lifrom autotest_lib.server.hosts import moblab_host
18*9c5db199SXin Lifrom autotest_lib.server.hosts import ssh_host
19*9c5db199SXin Li
20*9c5db199SXin Li
21*9c5db199SXin Li# TODO(kevcheng): Update the creation method so it's not a research project
22*9c5db199SXin Li# determining the class inheritance model (same for factory.create_host).
23*9c5db199SXin Lidef create_teststationhost(hostname, **kwargs):
24*9c5db199SXin Li    """Creates the TestStationHost object.
25*9c5db199SXin Li
26*9c5db199SXin Li    @param hostname: Hostname of the test station.
27*9c5db199SXin Li    @param kwargs: Keyword args to pass to the testbed initialization.
28*9c5db199SXin Li
29*9c5db199SXin Li    @return: A Test Station Host object.
30*9c5db199SXin Li    """
31*9c5db199SXin Li    classes = [TestStationHost]
32*9c5db199SXin Li    if hostname == 'localhost':
33*9c5db199SXin Li        classes.append(local_host.LocalHost)
34*9c5db199SXin Li    else:
35*9c5db199SXin Li        classes.append(ssh_host.SSHHost)
36*9c5db199SXin Li    host_class = type('new_teststationhost', tuple(classes), {})
37*9c5db199SXin Li    return host_class(hostname, **kwargs)
38*9c5db199SXin Li
39*9c5db199SXin Li
40*9c5db199SXin Liclass TestStationHost(base_classes.Host):
41*9c5db199SXin Li    """This class represents a linux box accessible via ssh."""
42*9c5db199SXin Li
43*9c5db199SXin Li
44*9c5db199SXin Li    def check_credentials(self, hostname):
45*9c5db199SXin Li        """Make sure teststation credentials work if we're doing ssh.
46*9c5db199SXin Li
47*9c5db199SXin Li        @param hostname: Hostname of the machine.
48*9c5db199SXin Li        """
49*9c5db199SXin Li        if hostname != 'localhost':
50*9c5db199SXin Li            try:
51*9c5db199SXin Li                self.run('true')
52*9c5db199SXin Li            except error.AutoservRunError:
53*9c5db199SXin Li                # Some test stations may not have root access, try user adb.
54*9c5db199SXin Li                logging.debug('Switching to user adb.')
55*9c5db199SXin Li                self.user = 'adb'
56*9c5db199SXin Li
57*9c5db199SXin Li
58*9c5db199SXin Li    def _initialize(self, hostname='localhost', *args, **dargs):
59*9c5db199SXin Li        """Initialize a Test Station Host.
60*9c5db199SXin Li
61*9c5db199SXin Li        This will create a Test Station Host. Hostname should always refer
62*9c5db199SXin Li        to the host machine connected to the devices under test.
63*9c5db199SXin Li
64*9c5db199SXin Li        @param hostname: Hostname of the machine, default to localhost.
65*9c5db199SXin Li        """
66*9c5db199SXin Li        logging.debug('Initializing Test Station Host running on host: %s.',
67*9c5db199SXin Li                      hostname)
68*9c5db199SXin Li
69*9c5db199SXin Li        # Do parent class initializations.
70*9c5db199SXin Li        super(TestStationHost, self)._initialize(hostname=hostname, *args,
71*9c5db199SXin Li                                                 **dargs)
72*9c5db199SXin Li
73*9c5db199SXin Li        self.check_credentials(hostname)
74*9c5db199SXin Li
75*9c5db199SXin Li        # We'll want to do certain things differently if we're on a moblab.
76*9c5db199SXin Li        self._is_host_moblab = None
77*9c5db199SXin Li        # Keep track of whether the host was closed since multiple AdbHost
78*9c5db199SXin Li        # might have an instance of this teststation.
79*9c5db199SXin Li        self._is_closed = False
80*9c5db199SXin Li
81*9c5db199SXin Li
82*9c5db199SXin Li    @property
83*9c5db199SXin Li    def is_moblab(self):
84*9c5db199SXin Li        """Check if the host running adb command is a Moblab.
85*9c5db199SXin Li
86*9c5db199SXin Li        @return: True if the host running adb command is a Moblab, False
87*9c5db199SXin Li                 otherwise.
88*9c5db199SXin Li        """
89*9c5db199SXin Li        if self._is_host_moblab is None:
90*9c5db199SXin Li            try:
91*9c5db199SXin Li                self.run('cat %s | grep -q moblab' % cros_constants.LSB_RELEASE)
92*9c5db199SXin Li                self._is_host_moblab = True
93*9c5db199SXin Li            except (error.AutoservRunError, error.AutotestHostRunError):
94*9c5db199SXin Li                self._is_host_moblab = False
95*9c5db199SXin Li        return self._is_host_moblab
96*9c5db199SXin Li
97*9c5db199SXin Li
98*9c5db199SXin Li    def get_tmp_dir(self, parent='/var/tmp'):
99*9c5db199SXin Li        """Return pathname of a temporary directory on the test station.
100*9c5db199SXin Li
101*9c5db199SXin Li        If parent folder is supplied and the teststation is a moblab.  Then
102*9c5db199SXin Li        the parent will have the moblab tmp directory prepended to it.
103*9c5db199SXin Li
104*9c5db199SXin Li        @param parent: The parent dir to create the temporary dir.
105*9c5db199SXin Li
106*9c5db199SXin Li        @return: Path of the newly created temporary dir.
107*9c5db199SXin Li        """
108*9c5db199SXin Li        if self.is_moblab:
109*9c5db199SXin Li            parent = (moblab_host.MOBLAB_TMP_DIR if parent == '/tmp'
110*9c5db199SXin Li                      else os.path.join(moblab_host.MOBLAB_TMP_DIR,
111*9c5db199SXin Li                                        parent.lstrip('/')))
112*9c5db199SXin Li        return super(TestStationHost, self).get_tmp_dir(parent=parent)
113*9c5db199SXin Li
114*9c5db199SXin Li
115*9c5db199SXin Li    def run(self, cmd, force_tty=True, *args, **dargs):
116*9c5db199SXin Li        """Run a command on the adb device.
117*9c5db199SXin Li
118*9c5db199SXin Li        This will run the command on the test station.  This method only
119*9c5db199SXin Li        exists to modify the command supplied if we're running a fastboot
120*9c5db199SXin Li        command on a moblab, otherwise we leave the command untouched.
121*9c5db199SXin Li
122*9c5db199SXin Li        @param cmd: The command line string.
123*9c5db199SXin Li        @param force_tty: Set to True to force pseudo-terminal allocation to
124*9c5db199SXin Li                run the command. This allows the command running on remote host
125*9c5db199SXin Li                to abort when the ssh command is timed out. Default is True.
126*9c5db199SXin Li
127*9c5db199SXin Li        @returns A CMDResult object or None if the call timed out and
128*9c5db199SXin Li                 ignore_timeout is True.
129*9c5db199SXin Li        """
130*9c5db199SXin Li        # TODO (sbasi/kevcheng) - Make teststation_host check if running
131*9c5db199SXin Li        # on ChromeOS, rather than MobLab when prepending sudo to fastboot.
132*9c5db199SXin Li        if cmd.startswith('fastboot ') and self.is_moblab:
133*9c5db199SXin Li            cmd = 'sudo -n ' + cmd
134*9c5db199SXin Li        if force_tty:
135*9c5db199SXin Li            dargs['options'] = dargs.get('options', '') + ' -t '
136*9c5db199SXin Li        return super(TestStationHost, self).run(cmd, *args, **dargs)
137*9c5db199SXin Li
138*9c5db199SXin Li    @retry.retry(error.GenericHostRunError, timeout_min=10)
139*9c5db199SXin Li    def download_file(self, src_url, dest_file, unzip=False, unzip_dest=None):
140*9c5db199SXin Li        """Download the given url.
141*9c5db199SXin Li
142*9c5db199SXin Li        @param src_url: The url to download from.
143*9c5db199SXin Li        @param dest_file: Destination for the file to be downloaded to.
144*9c5db199SXin Li        @param unzip: If True, unzip the downloaded file.
145*9c5db199SXin Li        @param unzip_dest: Location to unzip the downloaded file to. If not
146*9c5db199SXin Li                           provided, dest_file's directory is used.
147*9c5db199SXin Li
148*9c5db199SXin Li        @returns: The path of the downloaded file on the teststation.
149*9c5db199SXin Li        """
150*9c5db199SXin Li        try:
151*9c5db199SXin Li            self.run('wget -q -O "%s" "%s"' % (dest_file, src_url))
152*9c5db199SXin Li
153*9c5db199SXin Li            readlink_result = self.run('readlink -f "%s"' % dest_file)
154*9c5db199SXin Li            full_path = readlink_result.stdout.splitlines()[0]
155*9c5db199SXin Li
156*9c5db199SXin Li            if unzip:
157*9c5db199SXin Li                unzip_dest = unzip_dest or os.path.dirname(full_path)
158*9c5db199SXin Li                self.run('unzip "%s" -x -d "%s"' % (dest_file, unzip_dest))
159*9c5db199SXin Li
160*9c5db199SXin Li            return full_path
161*9c5db199SXin Li        except:
162*9c5db199SXin Li            # Delete the destination file if download failed.
163*9c5db199SXin Li            self.run('rm -f "%s"' % dest_file)
164*9c5db199SXin Li            raise
165