Home | History | Annotate | Download | only in cros
      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 
      5 import logging
      6 
      7 from autotest_lib.server import autotest
      8 from autotest_lib.server.cros import tradefed_constants as constants
      9 
     10 
     11 class 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