• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6
7from autotest_lib.server import autotest
8from autotest_lib.server.cros import tradefed_constants as constants
9
10
11class ChromeLogin(object):
12    """Context manager to handle Chrome login state."""
13
14    def need_reboot(self):
15        """Marks state as "dirty" - reboot needed during/after test."""
16        logging.info('Will reboot DUT when Chrome stops.')
17        self._need_reboot = True
18
19    def need_restart(self):
20        """Marks state as "dirty" - restart needed after test."""
21        self._need_restart = True
22
23    def __init__(self, host, kwargs):
24        """Initializes the _ChromeLogin object.
25
26        @param reboot: indicate if a reboot before destruction is required.
27        @param restart: indicate if a restart before destruction is required.
28        @param board: optional parameter to extend timeout for login for slow
29                      DUTs. Used in particular for virtual machines.
30        """
31        self._host = host
32        self._cts_helper_kwargs = kwargs
33        # We will override reboot/restart options to some degree. Keep track
34        # of them in a local copy.
35        self._need_reboot = False
36        if kwargs.get('reboot'):
37            self.need_reboot()
38        self._need_restart = False
39        if kwargs.get('restart'):
40            self.need_restart()
41        self._timeout = constants.LOGIN_DEFAULT_TIMEOUT
42        board = kwargs.get('board')
43        if board in constants.LOGIN_BOARD_TIMEOUT:
44            self._timeout = constants.LOGIN_BOARD_TIMEOUT[board]
45
46    def _cmd_builder(self, verbose=False):
47        """Gets remote command to start browser with ARC enabled."""
48        # If autotest is not installed on the host, as with moblab at times,
49        # getting the autodir will raise an exception.
50        cmd = autotest.Autotest.get_installed_autodir(self._host)
51        cmd += '/bin/autologin.py --arc'
52        if self._cts_helper_kwargs.get('dont_override_profile'):
53            logging.info('Using --dont_override_profile to start Chrome.')
54            cmd += ' --dont_override_profile'
55        else:
56            logging.info('Not using --dont_override_profile to start Chrome.')
57        if not verbose:
58            cmd += ' > /dev/null 2>&1'
59        return cmd
60
61    def login(self, timeout=None, raise_exception=False, verbose=False):
62        """Logs into Chrome."""
63        if not timeout:
64            timeout = self._timeout
65        try:
66            # We used to call cheets_StartAndroid, but it is a little faster to
67            # call a script on the DUT. This also saves CPU time on the server.
68            self._host.run(
69                self._cmd_builder(),
70                ignore_status=False,
71                verbose=verbose,
72                timeout=timeout)
73            return True
74        except autotest.AutodirNotFoundError:
75            # Autotest is not installed (can happen on moblab after image
76            # install). Fall back to logging in via client test, which as a side
77            # effect installs autotest in the right place, so we should not hit
78            # this slow path repeatedly.
79            logging.warning('Autotest not installed, fallback to slow path...')
80            try:
81                autotest.Autotest(self._host).run_timed_test(
82                    'cheets_StartAndroid',
83                    timeout=2 * timeout,
84                    check_client_result=True,
85                    **self._cts_helper_kwargs)
86                return True
87            except:
88                # We were unable to start the browser/Android. Maybe we can
89                # salvage the DUT by rebooting. This can hide some failures.
90                self.reboot()
91                if raise_exception:
92                    raise
93        except:
94            # We were unable to start the browser/Android. Maybe we can
95            # salvage the DUT by rebooting. This can hide some failures.
96            self.reboot()
97            if raise_exception:
98                raise
99        return False
100
101    def __enter__(self):
102        """Logs into Chrome with retry."""
103        timeout = self._timeout
104        logging.info('Ensure Android is running (timeout=%d)...', timeout)
105        if not self.login(timeout=timeout):
106            timeout *= 2
107            # The DUT reboots after unsuccessful login, try with more time.
108            logging.info('Retrying failed login (timeout=%d)...', timeout)
109            self.login(timeout=timeout, raise_exception=True, verbose=True)
110        return self
111
112    def __exit__(self, exc_type, exc_value, traceback):
113        """On exit restart the browser or reboot the machine.
114
115        @param exc_type: Exception type if an exception is raised from the
116                         with-block.
117        @param exc_value: Exception instance if an exception is raised from
118                          the with-block.
119        @param traceback: Stack trace info if an exception is raised from
120                          the with-block.
121        @return None, indicating not to ignore an exception from the with-block
122                if raised.
123        """
124        if not self._need_reboot:
125            logging.info('Skipping reboot, restarting browser.')
126            try:
127                self.restart()
128            except:
129                logging.error('Restarting browser has failed.')
130                self.need_reboot()
131        if self._need_reboot:
132            self.reboot(exc_type, exc_value, traceback)
133
134    def restart(self):
135        # We clean up /tmp (which is memory backed) from crashes and
136        # other files. A reboot would have cleaned /tmp as well.
137        # TODO(ihf): Remove "start ui" which is a nicety to non-ARC tests (i.e.
138        # now we wait on login screen, but login() above will 'stop ui' again
139        # before launching Chrome with ARC enabled).
140        script = 'stop ui'
141        script += '&& find /tmp/ -mindepth 1 -delete '
142        script += '&& start ui'
143        self._host.run(script, ignore_status=False, verbose=False, timeout=120)
144
145    def reboot(self, exc_type=None, exc_value=None, traceback=None):
146        """Reboot the machine.
147
148        @param exc_type: Exception type if an exception is raised from the
149                         with-block.
150        @param exc_value: Exception instance if an exception is raised from
151                          the with-block.
152        @param traceback: Stack trace info if an exception is raised from
153                          the with-block.
154        @return None, indicating not to ignore an exception from the with-block
155                if raised.
156        """
157        logging.info('Rebooting...')
158        try:
159            self._host.reboot()
160            self._need_reboot = False
161        except Exception:
162            if exc_type is None:
163                raise
164            # If an exception is raise from the with-block, just record the
165            # exception for the rebooting to avoid ignoring the original
166            # exception.
167            logging.exception('Rebooting failed.')
168