Home | History | Annotate | Download | only in cellular_StaleModemReboot
      1 # Copyright (c) 2014 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 import time
      7 
      8 from autotest_lib.client.bin import utils
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.server import test
     11 
     12 _MODEM_WAIT_DELAY = 120
     13 
     14 NO_MODEM_STATE_AVAILABLE = 'FAILED TO GET MODEM STATE'
     15 
     16 # TODO(harpreet / benchan): Modify the modem script to report modem health.
     17 # crbug.com/352351
     18 MM_MODEM_STATE_FAILED = '-1'
     19 MM_MODEM_STATE_UNKNOWN = '0'
     20 MM_MODEM_STATE_INITIALIZING = '1'
     21 MM_MODEM_STATE_LOCKED = '2'
     22 MM_MODEM_STATE_DISABLED = '3'
     23 MM_MODEM_STATE_DISABLING = '4'
     24 MM_MODEM_STATE_ENABLING = '5'
     25 MM_MODEM_STATE_ENABLED = '6'
     26 MM_MODEM_STATE_SEARCHING = '7'
     27 MM_MODEM_STATE_REGISTERED = '8'
     28 MM_MODEM_STATE_DISCONNECTING = '9'
     29 MM_MODEM_STATE_CONNECTING = '10'
     30 MM_MODEM_STATE_CONNECTED = '11'
     31 
     32 GOBI_MODEM_STATE_UNKNOWN = '0'
     33 GOBI_MODEM_STATE_DISABLED = '10'
     34 GOBI_MODEM_STATE_DISABLING = '20'
     35 GOBI_MODEM_STATE_ENABLING = '30'
     36 GOBI_MODEM_STATE_ENABLED = '40'
     37 GOBI_MODEM_STATE_SEARCHING = '50'
     38 GOBI_MODEM_STATE_REGISTERED = '60'
     39 GOBI_MODEM_STATE_DISCONNECTING = '70'
     40 GOBI_MODEM_STATE_CONNECTING = '80'
     41 GOBI_MODEM_STATE_CONNECTED = '90'
     42 
     43 ENABLED_MODEM_STATES = [
     44     MM_MODEM_STATE_ENABLED,
     45     GOBI_MODEM_STATE_ENABLED
     46 ]
     47 
     48 MM_STABLE_MODEM_STATES = [
     49     MM_MODEM_STATE_DISABLED,
     50     MM_MODEM_STATE_REGISTERED,
     51     MM_MODEM_STATE_CONNECTED,
     52 ]
     53 
     54 GOBI_STABLE_MODEM_STATES = [
     55     GOBI_MODEM_STATE_DISABLED,
     56     GOBI_MODEM_STATE_REGISTERED,
     57     GOBI_MODEM_STATE_CONNECTED
     58 ]
     59 
     60 class cellular_StaleModemReboot(test.test):
     61     """
     62     Uses servo to cold reboot the device if modem is not available or is not in
     63     testable state.
     64 
     65     The test attempts to get modem status by running the 'modem status' command
     66     on the DUT. If it is unsuccessful in getting the modem status or the modem
     67     is in a bad state, it will try to reboot the DUT.
     68 
     69     """
     70 
     71     version = 1
     72 
     73     def _modem_state_to_string(self, state, is_gobi):
     74         """Takes the numerical modem state and returns associated state name.
     75 
     76         @param state: The state of the modem on the device.
     77         @param is_gobi: True if the device has a gobi modem.
     78         @return MODEM_STATE_STRINGS as the actual name of the state associated
     79                 with the numeric value.
     80 
     81         """
     82         if not state:
     83             return NO_MODEM_STATE_AVAILABLE
     84 
     85         if is_gobi:
     86             MODEM_STATE_STRINGS = [
     87                 'UNKNOWN',
     88                 'DISABLED',
     89                 'DISABLING',
     90                 'ENABLING',
     91                 'ENABLED',
     92                 'SEARCHING',
     93                 'REGISTERED',
     94                 'DISCONNECTING',
     95                 'CONNECTING',
     96                 'CONNECTED'
     97             ]
     98             return MODEM_STATE_STRINGS[int(state[:1])]
     99 
    100         MODEM_STATE_STRINGS = [
    101             'FAILED',
    102             'UNKNOWN',
    103             'INITIALIZING',
    104             'LOCKED',
    105             'DISABLED',
    106             'DISABLING',
    107             'ENABLING',
    108             'ENABLED',
    109             'SEARCHING',
    110             'REGISTERED',
    111             'DISCONNECTING',
    112             'CONNECTING',
    113             'CONNECTED'
    114         ]
    115         return MODEM_STATE_STRINGS[int(state) + 1]
    116 
    117 
    118     def _format_modem_status(self, modem_status):
    119         """Formats the modem status data and inserts it into a dictionary.
    120 
    121         @param modem_status: Command line output of 'modem status'.
    122         @return modem status dictionary
    123 
    124         """
    125 
    126         modem_state = ''
    127         modem_status_dict = {}
    128 
    129         if not modem_status:
    130             return None
    131 
    132         lines = modem_status.splitlines()
    133 
    134         for item in lines:
    135             columns = item.split(':')
    136             columns = [x.strip() for x in columns]
    137             if len(columns) > 1:
    138                 modem_status_dict[columns[0]] = columns[1]
    139             else:
    140                 modem_status_dict[columns[0]] = ''
    141 
    142         return modem_status_dict
    143 
    144 
    145     def _get_modem_status(self):
    146         """Gets the status of the modem by running 'modem status' command.
    147 
    148         @return modem_status_dict: is the dictionary of all the lines retuned
    149                 by modem status command.
    150 
    151         """
    152         try:
    153             modem_status = self._client.run('modem status').stdout.strip()
    154             modem_status_dict = self._format_modem_status(modem_status)
    155             return modem_status_dict
    156         except error.AutoservRunError as e:
    157             logging.debug("AutoservRunError is: %s", e)
    158             return None
    159 
    160 
    161     def _get_modem_state(self):
    162         modem_status_dict = self._get_modem_status()
    163 
    164         if not modem_status_dict:
    165             return None
    166 
    167         return modem_status_dict.get('State')
    168 
    169 
    170     def _cold_reset_dut(self, boot_id):
    171         self._servo.get_power_state_controller().power_off()
    172         self._servo.get_power_state_controller().power_on()
    173         time.sleep(self._servo.BOOT_DELAY)
    174         self._client.wait_for_restart(old_boot_id=boot_id)
    175         self._wait_for_stable_modem_state()
    176 
    177 
    178     def _wait_for_stable_modem_state(self):
    179         """
    180         Wait for a maximum of _MODEM_WAIT_DELAY seconds for the modem to get
    181         into stable state. Also, because we do not want the test to terminate
    182         in case there is an exception, we are catching and logging the exception
    183         so the test can continue with rebooting the device again as needed.
    184 
    185         """
    186         try:
    187             utils.poll_for_condition(
    188                   lambda: self._get_modem_state() in self.STABLE_MODEM_STATES,
    189                   exception=utils.TimeoutError('Modem not in stable state '
    190                                                'after %s seconds.' %
    191                                                _MODEM_WAIT_DELAY),
    192                   timeout=_MODEM_WAIT_DELAY,
    193                   sleep_interval=5)
    194         except utils.TimeoutError as e:
    195             logging.debug("Stable modem state TimeoutError is: %s", e)
    196 
    197 
    198     def run_once(self, host, tries=2, expect_auto_registration=True):
    199         """
    200         Runs the test.
    201 
    202         @param host: A host object representing the DUT.
    203         @param tries: Maximum number of times test will try to reboot the DUT.
    204                 Default number of tries is 2, which is set in the control file.
    205         @param expect_auto_registration: To be used with an exceptional modem
    206                 that does not auto-register by passing False from modem specific
    207                 control file.
    208         @raise error.TestFail if modem cannot be brought to a testable stated.
    209 
    210         """
    211 
    212         self._client = host
    213         self._servo = host.servo
    214 
    215         if not self._servo:
    216             logging.info('Host %s does not have a servo.', host.hostname)
    217             return
    218 
    219         self.STABLE_MODEM_STATES = MM_STABLE_MODEM_STATES
    220         gobi = False
    221         if 'gobi' in self._client.run('modem status').stdout.strip().lower():
    222             self.STABLE_MODEM_STATES = GOBI_STABLE_MODEM_STATES
    223             gobi = True
    224 
    225         if not expect_auto_registration:
    226             self.STABLE_MODEM_STATES.extend(ENABLED_MODEM_STATES)
    227 
    228         original_modem_state = self._get_modem_state()
    229 
    230         logging.info('Modem state before reboot on host %s: %s',
    231                      host.hostname,
    232                      self._modem_state_to_string(original_modem_state, gobi))
    233 
    234         boot_id = self._client.get_boot_id()
    235 
    236         num_tries = 0
    237 
    238         while True:
    239             self._cold_reset_dut(boot_id)
    240             new_modem_state = self._get_modem_state()
    241             if new_modem_state in self.STABLE_MODEM_STATES:
    242                 logging.info('Modem is in testable state: %s',
    243                              self._modem_state_to_string(new_modem_state, gobi))
    244                 break
    245             if new_modem_state == MM_MODEM_STATE_LOCKED:
    246                 raise error.TestFail('Modem in locked state.')
    247             if num_tries == tries:
    248                 logging.info('Modem still in bad state after %s reboot tries '
    249                              'on host %s. Modem state: %s ',
    250                              tries+1, host.hostname,
    251                              self._modem_state_to_string(new_modem_state, gobi))
    252                 raise error.TestFail('Modem is not in testable state')
    253             num_tries += 1
    254